最近在看公众号的时候看到一篇文章,讲的是在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);
}
测试结果如下:
位域赋值分析见下图: