位段 字节边界对齐 算法 c语言,C语言-结构体-位域

1.位域的概念

在程序中,某些信息存储时不需要一个完整的字节,只需要几位,为节省存储空间C语言支持“位域”的结构体。具体说就是,将一个字节分为几个段,每一段表示一个对象,这样一个字节就可以表示多个对象。位段成员必须声明为int、unsigned int或signed int类型(short char long)。位域在本质上就是一种结构类型。

2.位域的定义

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

struct 位域结构名

{

位域列表

};

其中位域列表形式为: 类型说明符 位域名:位域长度

举例:

struct bs

{

int a:8;

int b:2;

int c:6;

};

位域变量的声明与结构变量声明的方式相同:先定义后说明,同时定义说明,直接声明。

3.位域的说明

1. 位域可以没有名字的,这时它只用来作填充或调整位置。无名的位域是不能使用的

struct bs

{

unsigned char a:4

unsigned char :0 /*空域*/

unsigned char b:4 /*从下一单元开始存放*/

unsigned char c:4

}

2. 宽度为 0 的一个未命名位域强制下一位域对齐到其下一type位域的边界

struct BitFiled_1

{

unsigned a : 4; // 第一个unsigned int,占4Byte

unsigned : 0; // 未命名位域

unsigned b : 4; // 从第二个unsigned int的4Byte开始存放,占4位

unsigned c : 4; // 还是第二个unsigned int中的4位

// 该位域结构总共 16 Byte

};

3. 位域的长度不能大于其类型说明符中指定类型的固有长度,比如说int类型的位域长度不能超过32(bit),char的位域长度不能超过8(bit)

struct BitField_2

{

int a : 33; // 编译错误,C2034,BitField_2::a位域类型的位数太小, >32

char b : 10; // 编译错误,C2034,BitField_2::b位域类型的位数太小, >8

};

4. 位域的存储

位域有一个非常重要的用途就是压缩存储,即:能够用1个比特解决的问题,绝不用2个比特。

1. 整个位域结构体的总大小为最宽基本类型成员大小的整数倍,这一点与常规结构体类型是一致的,从这里也可看出,位域本质上就是结构体;

2. 如果相邻位域字段的类型相同,且其声明的位宽长度之和小于类型的大小(sizeof获取的大小),则后面的位域字段将紧邻前一个字段存储,直到不能容纳为止;

// 假如 BitField_3::a = 0x11,(0001 0001 B); BitField_3::b = 0x2,(10 B);

// BitField_3::c = 0x35,(11 0101 B), 则有:

// 第一个4字节为:0001 0001 0000 0010 0000 0000 0000 0000(B)

// 第二个4字节为:0011 0101 0000 0000 0000 0000 0000 0000(B)

struct BitField_3

{

int a: 8; // 第一个4Byte中的开始8bit

int b: 2; // 由于相邻两个位域的类型相同,总大小10 < 32(int类型大小)

// 所以这里存储是挨着第一个4Byte,紧接着的2bit

char c: 6; // 由于相邻的两个位域类型不同

// 所以这里是第二个4Byte

// 总共8字节

};

3. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的位域字段将从新的存储单元开始,其起始偏移量为类型大小的整数倍;

// 假如 BitField_4::a = 0x1,(0001 B); BitField_4::b = 0x08676665

// 则有:第一个4字节:0000 0001 0000 0000 0000 0000 0000 0000(B)

// 第二个4字节:0x65 66 67 08(小端模式,高高低低,高字节存放在高地址)

struct BitField_4

{

int a: 4; // 第一个4字节

int b: 29; // 虽然相邻位域类型相同

// 但是,4 + 29 > 32

// 因此b位域从第二个4字节开始存储

// 总共8字节

};

4. 如果相邻位域字段的类型不同,则各编译器的具体实现有差异,VC采取不压缩方式

// 假如 BitField_5::a = 0x4, (0100 B); BitField_5::b = 0x5, (0101 B), 则有:

// 第一个4字节:0000 0100 0000 0000 0000 0000 0000 0000(B)

// 第二个4字节:0000 0101 0000 0000 0000 0000 0000 0000(B)

struct BitField_5

{

int a : 4; // 占据int的4字节中的4个bit

char b : 4; // 相邻字段类型不同,就算类型是char,也另起一个4字节

// 总共8Byte,可见不但没有压缩,还浪费空间

};

5. 如果位域字段之间穿插着非位域字段,则不进行压缩;

// 假如 BitField_6::a = 0x4,(0100 B); BitField_6::b = 0x65, (0110 0101 B)

// BitField_6::c = 0x3,(0011 B), 则有:

// 第一个字节: 0000 0100 (B)

// 第二个字节: 0110 0101 (B)

// 第三个字节: 0000 0011 (B)

struct BitField_6

{

char a: 4; // 第1个字节

char b; // 第2个字节,非位域

char c: 4; // 第3个字节

};

注意:如果不是位域字段之间穿插着非位域字段,如下面这种情况,是进行压缩的:

// 以下为两个字节,可见进行了压缩存储

// 如果把 char a; 放到最后,只要不在中间,也是会压缩存储的

struct BitField_7

{

char a; // 第1个字节

char b: 4; // 第2个字节中的4位

char c: 4; // 第2个字节紧挨着的4位

};

6. 当使用有符号类型来定义位域,并且无意中使用到了正负(有意或者无意)特性时,就有问题了。

struct BitField_8

{

char a : 2;

char b : 3;

char c : 3;

};

struct BitField_8 BF8;

// 位域赋值

BF8.a = 0x3; // 11

BF8.b = 0x5; // 101

BF8.c = 0x2; // 010

printf("%d,%d,%d\n", BF8.a, BF8.b, BF8.c);

// OUTPUT: -1(0xff, 1111 1111), -3(0xfd, 1111 1101), 2(0x02, 0000 0110)

// 可见,当为域的最高位是1的时候,会进行符号扩展,而且这也取决于编译器的实现

// 因此,为避免此类问题,最好使用无符号类型定义位域

// 如果把BitField_8中的char换成unsigned char就没有问题了,输出是3, 5, 2

从其内存布局可以看出,使用位域的最佳实践是:第一,位域的类型要使用无符号类型,并且在整个结构体内部要保持一致;第二,位域的总长度尽量与类型的长度保持一致;第三,不要在两个位域中间穿插非位域字段;如下代码所示:

struct BitFieldDemo

{

unsigned char a : 2;

unsigned char b : 3;

unsigned char c : 3;

};

参考:

1. https://jocent.me/2017/07/24/bit-field-detail.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值