位域结构体问题

最近在看公众号的时候看到一篇文章,讲的是在socket数据结构体解析的时候,出现位域成员赋值和预期不一致的情况。正好近期用到位域的用法,故实验并记录于下。

1、位域的概念及定义(复制于该公众号文章)

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。

例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。

所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。

每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

位域定义与结构定义相仿,其形式为:

struct 位域结构名
{
  类型说明符 位域名: 位域长度;  
};

对于位域的定义尚有以下几点说明:

一个位域必须存储在同一个字节中,不能跨两个字节

如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:

struct bit
{
    int a:4;
    int :0;    //空域
    int b:4;   //从下一个单元开始存放
    int b:4;   //
};

在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。

位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

struct bit
{
    int a:1;
    int :2; //这2位不可使用
    int b:3;
    int c:2;
};

从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。

这是位域操作的表示方法,也就是说后面加上“:1”的意思是这个成员的大小占所定义类型的1 bit,“:2”占2 bit,依次类推。当然大小不能超过所定义类型包含的总bit数。

一个bytes(字节)是8个 bit(二进制位)。例如你的结构体中定义的类型是u_char,一个字节,共8个bit,最大就不能超过8。32位机下,short是2字节,共16bit,最大就不能超过16,int是4字节,共32bit,最大就不能超过32. 依次类推。

位域定义比较省空间。

struct fc_bit
{
    //byte
    unsigned char a:4;
    unsigned char b:2;
    unsigned char c:2;
    //byte
    unsigned char d:1;
    unsigned char e:1;
    unsigned char f:1;
    unsigned char g:1;
    unsigned char h:1;
    unsigned char i:1;
    unsigned char j:1;
    unsigned char k:1;
};

例如上面的结构,定义的变量类型是unsigned char,是一字节类型,即8bit。

a占了4bit,b占2bit,c占2bit,共8bit,正好是一个字节。

其他八个成员,各占1bit,共8bit,正好也是一个字节。

因此你的结构的大小如果用sizeof(struct fc_bit)计算,就是2bytes。

2、实验代码

#include <stdio.h>
#include <string.h>

typedef struct
{
    unsigned char a : 1;
    unsigned char b : 3;
    unsigned char c : 4;
    unsigned char d : 1;
    unsigned char e : 7;
} BIT;

typedef struct
{
    unsigned char a : 5;
    unsigned char : 0;
    unsigned char b : 1;
    unsigned char c : 7;
} TEST_BIT;
int main()
{
    unsigned char *char_p;
    unsigned short *short_p;
    unsigned short temp_number = 0;
    const unsigned short test_number = 0x815a;
    BIT bit;
    TEST_BIT test_bit;

    /*********测试位域赋值*********/
    memset(&bit, 0, 2);
    //结构体指针转化成char指针
    char_p = (unsigned char *)&bit;
    char_p[0] = 0x5a;
    char_p[1] = 0xa5;
    printf("a = 0x%02x\t b = 0x%02x\t c = 0x%02x\t d = 0x%02x\t e = 0x%02x\r\n", bit.a, bit.b, bit.c, bit.d, bit.e);

    /*********测试大小端*********/
    memset(&bit, 0, 2);
    //结构体指针转化成short指针
    short_p = (unsigned short *)&bit;
    *short_p = test_number;
    printf("a = 0x%02x\t b = 0x%02x\t c = 0x%02x\t d = 0x%02x\t e = 0x%02x\r\n", bit.a, bit.b, bit.c, bit.d, bit.e);
    temp_number = bit.d | bit.e;
    if ((test_number & 0x00ff) == temp_number)
        printf("Big Endian\r\n");
    else
        printf("Little Endian\r\n");

    /*********测试未使用的位域*********/
    memset(&test_bit, 0, 2);
    //结构体指针转化成char指针
    char_p = (unsigned char *)&test_bit;
    char_p[0] = 0xaa;
    char_p[1] = 0xaa;
    printf("test_a = 0x%02x\t test_b = 0x%02x\t test_c = 0x%02x\r\n", test_bit.a, test_bit.b, test_bit.c);
}

测试结果如下:

位域赋值分析见下图:

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值