C99规定int、unsigned int和bool可以作为位域类型。但编译器几乎都对此作了扩展,允许其它类型类型的存在。
如果结构体中含有位域(bit-field),总结规则如下:(以下代码在x86 32bit系统上测试,gcc 4.1.2)
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止
例:
struct test1 {
char a : 2;
char b : 3;
char c : 3;
};
3个变量正好占1个字节,所以整体只要占1个字节
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍
例:
struct test2 {
char a : 2;
char b : 3;
char c : 7;
};
3个变量超过1个字节,a和b在占一个字节,c占一个字节,所以整体占2个字节。
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式
例1:
struct test3
{
char a:1;
char b:1;
long c:1;
char d:2;
};
4个变量<=1个字节,所以整体占1个字节,对吧!
错了,开始我也是这样想的,结果是4个。
我猜测了一下:
虽然a,b,c,d加起来只占1个字节,但是最后结构体整体也要进行对齐。
详细可以查看之前写的不含位域的内存对齐说明http://blog.csdn.net/todd911/article/details/6528428,
对齐规则的第二条:结构(或联合)的整体对齐规则:在 数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
这边因为没有定义#pragma pack,所以默认按4个字节进行对齐,结构体中最大的数是long,占4个字节,所以最终要按4个字节进行整体对齐。
我们来验证一下,如果将结构体改为:
struct test3
{
char a:1;
char b:1;
short c:1;
char d:2;
};
结果应该是2。
试一下,确实是2.
再来修改一下:
struct test3
{
char a:1;
char b:1;
long c:31;
char d:2;
};
上面的结构体占12个字节。
a,b合起来占1个字节,c如果要合到a,b中去,的话就是33bit,超过将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个,
所以c只能自立门户,从第5个字节(偏移量为32bit)开始放置占4个字节,同理最后d也合不进c中去,所以从第9个字节开始放置,最后结构体整体对齐,结果是12.
总结gcc的压缩方式:
a.先要获得#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个值,这边称为对齐基数。
b.按照结构体中变量的顺序,如果能合在一起且不超过a中的对齐基数的就合并,不能合并的需要自立门户,偏移量为对齐基数的整数倍,继续检查是否能进行合并。。。
c.最后结构体整体进行对齐。
4)如果位域字段之间穿插着非位域字段,非位域和位域字段不进行压缩,按照不包含位域的结构体进行对齐,位域与位域之间进行压缩,
详细规则可以参考我以前写的文章:http://blog.csdn.net/todd911/article/details/6528428
例:
struct test4
{
char a:1;
char b:1;
char c:1;
char d:1;
short e;
};
占用4个字节。