有些信息在存储时,并不需要占用一个完整的字节,而只需要占用一个或几个二进制位。
例如: 存放一个开关量时,只有0和1二种状态,只保存一位二进制位即可。为了节省存储空间,C语言提供了数据结构:位域。
位域是把字节中的二进制位划分为几个不同的区域,并说明每个区域的位数。每个区域有个域名,允许在程序中按域名
进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
位域的定义
位域定义与结构体定义相仿,形式为:
struct 位域结构名
{
类型名 位域名: 位域长度,如 int a 8; //位域长度不能大于8(即一个字节)
......
};
注意:
一个位域必须存储在一个字节中,当有二个连续的位域,在一个字节中存放第一个位域后剩下的空间不足以存放第二个
位域,则第二个位域将存放在下一个字节单元中,前一个字节多余的位补0。(因为一个位域必须存储在一个字节,所以位域长度最大为8)
1 struct wy
2 {
3 unsigned int a : 4; //第一个字节前4位存放该位域
4 unsigned int b : 5; //第一个字节剩下的4位不足以存放b,位域b从下一个字节开始
5 }WY;
6 sizeof(WY) == 4;位域存储的类型是int占用4个字节,根据内存对齐规则不足4字节也占用4字节。对齐规则可以往下看
空域:
不管当前字节单元剩下的位数是否足以存放下一个位域,使用空域后,下一个位域都存放到下一个类型单元,比如int就存放到下个int单元。
1 struct wyn
2 {
3 unsigned int a : 4; //第一个字节前4位存放该位域
4 unsigned int :0;
5 unsigned int b : 2; //空域后的位域从下个单元开始
6 }WYN;
7 sizeof(WYN) == 8; 因为存储的类型是int型,空域后,从下个int开始
wyn nn; nn.a = 6; //给a赋值为6其实是把a占用的4位赋值为 0110
当一个位域无位域名时,它只是用来作为填充或调整位置,不能被使用(比如 int :3 仅仅是用来占3位二进制位,但这3位没法在程序里使用)。
位域内存对齐规则
1 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof(类型)大小,则后面的字段紧邻前一个字段存储。
2 如果相邻位域字段的类型相同,且其位宽之和大于类型的sizeof(类型)大小,则后面的字段从新的存储单元开始,其偏移量为sizeof(类型)大小的整数倍
3 如果相邻位域字段的类型不同,则各编译器的具体实现有差异,需要在编译器上测试
1 struct wyn
2 {
3 unsigned int i : 8;
4 unsigned int j : 4;
5 unsigned int a : 3;
6 double b;
7 }WYN;
8 sizeof(WYN) == 16;
i,j,a共占用2个字节,根据规则1,紧邻的int型i,j,a共占用4字节。
b位double型,占用8字节,根据对齐规则,b的内存地址必须是8的整数倍,所以在i,j,a后会填充4个字节,再存储b。
1 struct wyn2
2 {
3 unsigned int i : 8;
4 unsigned int j : 4;
5 double b;
6 unsigned int a : 3;
7 }WYN2;
8 sizeof(WYN2) == 24;
同上面WYN一样,根据规则1,2:i,j,b占用16字节
位域a占用3位,类型是int应该占用4字节,但是整个结构体大小应该按照最大的类型double对齐,必须是8的整数倍。所以占用24字节