一、结构体变量在内存中存放的位置,也就是对齐方式,默认情况下是由编译器决定的。如果我们需要对其进行更改,可以使用:
#pragma pack(n)
表示将结构体中的成员按n字节的对齐方式存储;
二、说明
- #pragma pack提供数据声明级别的控制,对定义不起作用
- 调用pack时不指定参数,n将被设定为默认值
- 一旦改变数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降
三、重要规则
- 复杂类型中各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个类型的地址相同;
- 每个成员分别对齐,即每个成员按自己的方式对齐,并最小化长度;规则就是每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐;
- 结构、联合或者类的数据成员,第一个放在偏移为0的地方;以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度两个中比较小的那个进行;也就是说,当#pragma pack指定的值等于或者超过所有数据成员长度的时候,这个指定值的大小将不产生任何效果;
- 复杂类型(如结构)整体的对齐<注意是“整体”>是按照结构体中长度最大的数据成员和#pragma pack指定值之间较小的那个值进行;这样在成员是复杂类型时,可以最小化长度;
- 结构整体长度的计算必须取所用过的所有对齐参数的整数倍,不够补空字节;也就是取所用过的所有对齐参数中最大的那个值的整数倍,因为对齐参数都是2的n次方;这样在处理数组时可以保证每一项都边界对齐
四、实例
在相同的对齐方式下,结构体内部数据定义的顺序不同,结构体整体占据内存空间也不同,
如下: 设结构体如下定义:
struct A
{
int a;
char b;
short c;
};
结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个。所以A用到的空间应该是7字节。但是因为编译器要对数据成员在空间上进行对齐,也就是计算结构整体长度,根据上面三中的第5点可知,整体长度就是所有对齐参数中最大的那个值(int 4字节)的整数倍,此时应该为2 * 4 = 8,所以使用sizeof(strcut A)值为8。
现在把该结构体调整成员变量的顺序。
struct B
{
char b;
int a;
short c;
};
这时候同样是总共7个字节的变量,但是sizeof(struct B)的值却是12,
地址分配为:b: 0x0000 0000(0x0000 0000), a: 0x0000 0004(0x0000 0004 ~ 0x0000 0007), c: 0x0000 0008(0x0000 0008 ~ 0x0000 0009);计算结构体整体长度,就是3 * 4 = 12;
下面我们使用预编译指令#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。
#pragma pack (2) /*指定按2字节对齐,等价于#pragma pack(push,2)*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /取消指定对齐,恢复缺省对齐,等价于#pragma pack(pop)/
sizeof(struct C)值是8;地址分配:b: 0x0000 0000;a: 0x0000 0002(根据对齐规则,int自身的对齐长度为4,使用pack指定为2,取其中的最小值,也就是2字节对齐);c: 0x0000 0006; 整体长度。
修改对齐值为1:
#pragma pack (1) /*指定按1字节对齐*/
struct D
{
char b;
int a;
short c;
};
#pragma pack () /取消指定对齐,恢复缺省对齐/
sizeof(struct D)值为7。
对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,long类型,其自身对齐值为4,double,long long类型,其自身对齐值为8,单位字节。
五、概念
这里面有四个概念值:
1.数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。
2.指定对齐值:#pragma pack (value)时的指定对齐值value。
3.结构体或者类的自身对齐值:其数据成员中自身对齐值最大的那个值。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。