结构体中的冒号表示位域,如:
struct bit_struct { unsigned int bit1:10; unsigned int bit2:8; unsigned int bit3:14; } data1; // sizeof data1 = 4 struct bit_struct { unsigned int bit1:10; unsigned int bit2:8; unsigned int bit3:16; } data2; // sizeof data2 = 8
位域出现的原因是由于某些信息的「存储表示」只需要几个 bit 位就可以表示,而不需要一个完整的字节,同时也是为了节省存储空间和方便处理。
位域必须存储在同一个类型中,不能跨类型,同时也说明位域的长度不会超过所定义类型的长度。如果一个定义类型单元里所剩空间无法存放下一个域,则下一个域应该从下一单元开始存放。
如 data2,所定义的类型是 unsigned int
类型,一共 32 位,bit1 和 bit2 用掉了 18bit,还剩下 \(32-18=14bit\),这时要存储一个 16bit 的位域元素 bit3,那么这个元素就只能从下一个 unsigned int
类型的单元开始而不会在前面一个unsigned int
类型中占 14bit 后面的 unsigned int 类型中占 2bit。
如果位域的位域长度为0表示是个空域,同时下一个域应当从下一个字节单元开始存放。
使用无名的位域来作为填充和调整位置,切记该位域是不能被使用的。
位域的本质上就是一种结构体类型,不同的是其成员是按二进制位来分配的。
#include <stdio.h> #include <string.h> struct bit_struct_1 { unsigned int bit1:10; unsigned int bit2:8; unsigned int bit3:14; } data1; // sizeof data1 = 4 struct bit_struct_2 { unsigned int bit1:10; unsigned int bit2:8; unsigned int bit3:16; } data2; // sizeof data2 = 8 /* 0001 1001 0010 1010 1011 0011 0100 1111 0101 1101 0110 1100 0111 1110 1000 1011 ————————— ————————— ————————— ————————— ————————— ————————— ————————— ————————— 19 2A B3 4F 5D 6C 8E 9B 高 <-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·- 低 */ int main() { unsigned long long ullNum = 0x192AB34F5D6C7E8B; memcpy(&data1, (void *)&ullNum, sizeof(data1)); memcpy(&data2, (void *)&ullNum, sizeof(data2)); printf("data1 size is %d\n", sizeof(data1)); /* size is 4 */ printf("[1] bit1 : %u\n", data1.bit1); /* 651 --> 1010001011 */ printf("[1] bit2 : %u\n", data1.bit2); /* 31 --> 00011111 */ printf("[1] bit3 : %u\n", data1.bit3); /* 5979 --> 01011101011011 */ puts("------------------------"); printf("data2 size is %d\n", sizeof(data2)); /* size is 8 */ printf("[2] bit1 : %u\n", data2.bit1); /* 651 --> 1010001011 */ printf("[2] bit2 : %u\n", data2.bit2); /* 31 --> 00011111 */ printf("[2] bit3 : %u\n", data2.bit3); /* 45903 --> 1011001101001111 */ return 0; }
上述代码中我们定义了一个 8B 的 ullNum,其二进制表示如下图所示:
从低字节到高字节分别分配给 bit1、bit2、bit3 :