一:结构体内存对齐规则
-
1.第一个成员在结构体变量偏移量为0的地址处。
-
2.从第二个成员变量开始,要对齐到对齐数的整数倍 地址处。
对齐数 就是 编译器默认的一个对齐数 与 该成员大小中的 较小的那一个。- VS编译器默认的对齐数是8(字节), Linux中默认对齐数是4
-
3.结构体总大小 是每个成员变量的对齐数 中最大的对齐数的整数倍。
-
4 如果结构体中嵌套了另外一个结构体,那么就把这个嵌套在里面的结构体当成外面结构体的一个成员,里面的结构体的对齐到自己的最大对齐数的整数倍,(这时里面的结构体也有一个最大对齐数,把这个对齐数就当做里面的结构体的对齐数),所以整体大小就是最大对齐数的整数倍。
-
注意:32位平台下,vs的默认对齐数是4字节,但是在64位平台下,vs的默认对齐数是8字节。
二:内存对齐的大小计算
如何修改默认对齐数:
修改默认对齐数: 使用 #pragma pack(n)
恢复默认对齐数: 使用 #pragma pack()
修改默认对齐数后的结构体大小:
//穿插一个结构体只是验证
struct S
{
float f; //4 ---4
short s; //2 ---2(4)
int i; //4 ----4
double d; //8 ----8(4+8)
char c; //1 ----1
int a[4]; //16 ----16(3+16)
//4+4+4+12+1+19=44, 结构体大小应该是48,经过验证,结果正确
};
struct B
{
float f; //4 ---4
short s; //2 ---2(4)
int i; //4 ----4
char c; //1 ----1
char a[7]; //7 ----7 ---对齐数是1,不是7,说明了数组的对齐数是对应元素的类型大小,而不是数组的大小
//4+4+4+1+7=20, 结构体大小应该是20,经过验证,结果正确
};
int main()
{
int a[4];
cout << sizeof(S) << endl; //40
cout << sizeof(B) << endl; //20
cout << sizeof(float) << endl; //4
cout << sizeof(short) << endl; //2
cout << sizeof(double) << endl; //8
cout << sizeof(a) << endl; //16
return 0;
}
三:结构体内存对齐原因
为什么要有结构体内存对齐?
原因:
(1)方便移植,因为不是所有的硬件平台都能任意访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则会抛出硬件异常。
(2)有利于性能的提升,如果内存没有对齐,处理器需要做两次内存访问;而对齐的内存的访问仅需要一次。所以数据结构应该尽可能的在自然边界上对齐。
(3)内存对齐实质上是一种以空间换区取时间的做法,因此在设计结构体的时候,应该既要满足对齐,又要节省空间,可以采取让占用空间小的成员尽可能的其中在一起。
联合体(共用体)
1.定义:
联合体是一种特殊的自定义类型,联合体变量包含一系列成员,这些成员公用同一份内存空间。
//联合体的声明
union Un
{
int a;
char b;
}
//联合体的定义
union Un un; //un就是联合体变量
//计算联合体大小
printf("%d\n",sizeof(un));
2.联合体大小的计算规则:
满足以下两点:
- 联合体的大小 >= 最大成员的大小
- 当最大成员大小不是最大对齐数的整倍数时, 要对齐到最大对齐数的整数倍(对齐的结果是超过并接近最大成员大小)。
例如:求联合体Un1的大小?
分析:
1.变量c 是个短整型数组,有七个元素,大小是14字节,vs编译器默认的对齐数是8字节,所以两者最终的对齐数(取较小的对齐数作为变量的对齐数)是8字节。
2.变量i 是个整型变量,大小是4字节,vs编译器默认的对齐数是8字节,所以最终变量i的对齐数是4字节。
3.所以,整个共用体的最大对齐数为变量c的对齐数----8。但是考虑到最大成员的大小14不是8的倍数,所以要对齐到8的整数倍----16。