引例
本次代码均运行在32位机,VS2019环境下
分析如下一段代码:
int main() {
struct S {
char c1;
int num;
char c2;
}s1;
printf("%d", sizeof(s1));
return 0;
}
理论上来讲这个结构体的大小就是各个成员变量大小的总和对吧?但是实际结构体的大小并不是这么回事! 实际输出是12个字节。这是为什么呢?这就是这次我们主要探讨的东西 “内存对齐”
内存对齐的规则
我们首先需要了解结构体内存对齐的规则:
1、第一个成员变量存放在与结构体地址偏移量为0的地址处,也就是首地址
2、其他成员要存放在对齐某个数字(对齐数)的整数倍地址处
对齐数:取编译器默认的一个对齐数和该成员变量大小 的较小值
- VS中默认对齐数为8
- Linux也就是Gcc编译器没有默认对齐数,对齐数就是该成员自身大小
3、结构体的总大小为最大对齐数(每个成员的对齐数,取其中对齐数最大的成员的对齐数)的整数倍。举个例子:若最大对齐数 8,结构体存储了11个字节,那么再往后申请5个字节的空间来对齐到16.
4、如果结构体内嵌套结构体了,嵌套的结构体对齐到自身成员的最大对齐数的整数倍,而结构体的整体大小就需要对齐到所有最大对齐数的整数倍处。
讲完规则现在我们回头来看下引例中这段代码的结构体在内存中到底是如何分布的:
这便是结构体s1在内存的分布情况,我们可以清楚的看到他分配了12个字节空间。在VS的调试界面也可以清楚看到:
为了方便观察我分别给成员变量赋了值
至此我们就大概了解了内存对齐的规则,下边我们再来思考 编译器为什么要浪费空间来这么干呢?这么做有什么好处吗?
-
平台原因(移植性):
不是所有的硬件平台都是可以从任意地方读取写入数据的,某些平台只能在某些特定的地址来操作数据,否则会抛出硬件异常 -
性能原因:
数据结构(特别是栈)应尽量将数据存放在自然边界上对齐。
原因在于:访问未对齐的数据处理器可能需要两次访问才能做到, 而对齐到数据只需要一次访问。
总体来讲,结构体这个数据类型就是用存储空间来换取存取速度的做法
编译器的默认对齐数是否可以修改?如何修改?
这个问题的答案是肯定的,他是可以修改的
我们可以通过如下两条代码来修改和恢复编译器默认的对齐数:
#pragma pack(1)//将对齐数修改为1
#pragma pack()//将对齐数恢复为默认值
一般情况下我们设置的对齐数为2的次方数,并且设计结构体成员的时候要尽量让占用空间较小的成员集中在一起,这样才能最大限度的节省空间并且提高效率!