文章目录
一、结构体内存对齐规则
1.第一个成员在与结构体变量偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍地址(偏移量) gcc编译器没有默认对齐数 成员大小就是对齐数
对齐数=编译器默认的一个对齐数(VS默认为8)与该成员大小之间的一个较小值
3.结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处, 结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
见下列代码:
struct S1//结构体变量
{
char c1;//1 c1 第一个成员
int a;//4 4<8 对齐数为4 此时4为该结构体的最大对齐数
char c2;//1 对齐数为1
};
struct S2
{
char c1;//1
char c2;//1
int a;//4
};
#include<stdio.h>
int main()
{
struct S1 s1 = {
0 };
printf("%d\n", sizeof(s1));
struct S2 s2 = {
0 };
printf("%d\n", sizeof(s2));
return 0;
}
运行结果:
12
8
解释(以S1为例):
-
1.第一个成员在与结构体变量偏移量为0的地址处。代码中的c1就是第一个成员,而c1占1个字节,所以c1存储在下图蓝色部分
-
2.其他成员变量要对齐到某个数字(对齐数)的整数倍地址(偏移量) gcc编译器没有默认对齐数 成员大小就是对齐数。
对齐数=编译器默认的一个对齐数(VS默认为8)与该成员大小之间的一个较小值 。
我用的是VS,所以默认对齐数为8。第二个成员c2的大小为4,4<8,所以c2的对齐数是4,所以c2从偏移量为4的倍数的数开始存储,因为偏移量4后还没存储任何数,所以c2从偏移量为4处开始存储,又c2占4个字节,所以c2存储在下图绿色部分 -
同理,c3存储在下图橙色部分
-
3.结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。将c1 c2 c3都按规则存储完后,应共占1+4+1=6个字节,但结果是12。因为按照前三条对齐规则,白色部分必须是空出来的,而按照本条对齐规则,因为c2的大小最大,为4,所以S1的最大对齐数为4,而按照前三条对齐规则存储完后,加上空白部分为9,最接近4的倍数12,所以应再加上三个空白部分,即黄色部分
-
综上所述,结构体S1的大小为12
-
通过运算结果我们发现,只是将c1和a的位置换了一下,结构体大小就从12变成了8,所以我们应让空间小的成员尽量集中在一起
- 4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处, 结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
见代码:
struct S3
{
double d;//8
char c;//1
int i;//4
};
struct S4
{
char c1;//1
struct S3 s3;//16 最大对齐数为8 从偏移量为8的时候开始
double d;//8
};
#include<stdio.h>
int main()
{
struct S3 s3 = {
0 };
printf("%d\n", sizeof(s3));
struct S4 s4 = {
0 };
printf("%d\n", sizeof(s4));
return 0;
}
运算结果:
16
32
解释:
- 按照对齐规则可求出S3的大小为16
- 计算S4大小时16>8,所以对齐数为8
- 可同理计算出S4的大小为32
二.为什么存在内存对齐
- 1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取得某些特定类型的数据,否则抛出硬件异常
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问只需要一次访问
总体来说,结构体的内存对齐是拿空间换取时间的做法 - 那在设计结构体时既要满足对齐又要节省空间,如何做到?
1.应让空间小的成员尽量集中在一起
2.修改默认对齐数 #pragma 一般为2的次方数