下列程序输出结果
struct A { int x: 8; int y: 4; int z: 3; double a; }; struct B { int x: 8; int y: 4; double a; int z: 3; }; void main() { printf("sizeof(A)=%d,sizeof(B)=%d\n",sizeof(A),sizeof(B)); }
输出结果 16和24;
位域
位域是为了节省存储空间而提出的一种数据结构,有些信息存储并不需要用到一个字节,只需1位或几位二进制即可,C++中提供了bitset容器,借鉴了位域的思想。
一个int类型有32个bit,对于开关量只有0与1的值,只需一个bit就可以,而无需用整个int类型去表示这个条件变量
所谓位域即将一个字节中的二进制划分为不同的区域,每个区域有不同的位域名和位数。空域或者用于调整位置,可以没有位域名。这样就允许将几个不同的对象用一个字节的位域来表示。
结构体中位域的定义形式
struct 结构体名
{ 位域列表 };
位域列表的形式:类型说明符位域名:位域长度
struct SA { int x: 8; int y: 4; int z: 3; }Sa;
Sa占用两个字节,其中x占8位,y占4位,z占3位,空着的1位没用
1. 位域的类型说明符只能为int或unsigned int
2. 位域不允许跨越2个字节,位域长度最多为8,且当上一个字节所剩的bit数无法存储下时,会从下一个字节开始存储
struct SB { int x: 8; int y: 4; int z: 5; }Sb;
Sb占用3个字节,z无法存储在剩下的4个bit中,只能从下一个字节开始存储。
3. 空域或者用于调整位置,允许没有位域名,此类域不能被使用
struct SC { int x: 6; int :0;/*空域*/ int y: 1;/*从下一字节开始存储*/ int :2;/*此两个字节不能使用*/ int z: 3; int w: 3; }Sc;
Sc占用3个字节
4. 位域在本质上就是一种结构类型, 不过其成员是按二进位分配的,位域的使用与普通的结构体内成员的使用方法一样
参考 http://www.cppblog.com/fwxjj/archive/2006/12/18/16572.html
结构体对齐
结构体数据成员对齐的意义
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的起始地址的值是某个数k的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。
struct B { int x: 8; int y: 4; double a; int z: 3; };
为什么struct B的类型为什么长度为24?double长度为8,x占一个字节,y也占一个字节,但为了与double对齐,会填充6个字节,而后面的z也一样,因此为24字节
结构体大小的计算方法和步骤
1)将结构体内所有数据成员的长度值相加,记为sum_a;
2)将各数据成员为了内存对齐,按各自对齐模数而填充的字节数累加到和sum_a上,记为sum_b。对齐模数是#pragma pack指定的数值以及该数据成员自身长度中数值较小者。该数据相对起始位置应该是对齐模式的整数倍;
3)将和sum_b向结构体模数对齐,该模数是#pragma pac指定的数值和结构体内部最大的基本数据类型成员长度中数值较小者。结构体的长度应该是该模数的整数倍。
程序中若没有定义#pragma pack编译参数,只考虑默认对齐模数即可
windows的默认对齐模数与数据类型长度相同,linux对于double和long double的默认对齐模数为4
另long double在windows和linux下的长度不一样,
若想更具体知道windows/linux如何计算可参见http://www.cnblogs.com/motadou/archive/2009/01/17/1558438.html