一.什么是内存对齐
在解决这个问题之前,我们先看一组代码,大家可以思考思考会输出什么:
//结构体1 struct S1 { char c1; int i; char c2; }; //结构体2 struct S2 { char c1; char c2; int i; }; printf("%zd\n", sizeof(struct S1)); printf("%zd\n", sizeof(struct S2));
我们可以看到俩个结构体的成员类型都是一模一样的,唯独不同的是成员直接的相互顺序不同,按道理来说应该输出的是同一个值,那结果真是这样吗,我们继续往下走
输出结果:
结果非常的出人意料啊,他们的字节大小居然不一样,造成这样现象的原因就是因为存在结构体内存对齐的情况,接下来我们就来探索一下什么是结构体内存对齐以及如何计算它
二.怎么对齐的
使用 offsetof 宏来观察
什么是 offsetof 宏,我们打开 cplusplus 官网:
通过查询资料得知,offsetof 是用来计算结构体中成员单位相较于起始位置的偏移量的一个宏,具体使用如下
结构体S1
//查看S1的成员的偏移量 printf("%zd\n", offsetof(struct S1, c1)); printf("%zd\n", offsetof(struct S1, i)); printf("%zd\n", offsetof(struct S1, c2));
输出结果如下:
那我们就根据图示画出内存中的大概模型
结构体S2
同样的对结构体 S2 也做这样的处理
printf("%zd\n", offsetof(struct S2, c1)); printf("%zd\n", offsetof(struct S2, c2)); printf("%zd\n", offsetof(struct S2, i));
输出结果:
根据图示画出内存中的大概模型
我们将俩个模型进行比较,并且结合输出观察
我们会发现
- 在结构体 S1 中浪费了很多空间,在char c1 后浪费了 3 个字节的空间,在char c2 后浪费了 3 个字节的空间
- 在结构体 S2 中,char c1 后并没有浪费空间,而在 char c2 后浪费了 2 个字节的空间
而这就是因为受到了结构体对其数的影响
三.如何计算对齐
首先得掌握结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为 0 的地址处
- 其他成员变量要要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值(VS中默认的值为8 )
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
我们具体拿结构体 S1 来进行演示说明
第一步:
第一个成员在与结构体变量偏移量为0的地址处
第二步:
放置 int i 大小为 4 个字节和 VS 默认的对齐数 8 相比,取较小的数 4,所以下一个元素 int i 对齐到从起始位置数第 4 个单元的位置
第三步:
放置 char c2 数据,char 大小为1个字节和 VS 默认的 8 相比,取较小的数 1 ,对齐数就是 1
第四步:
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,在这个结构体里面,3 个成员的大小分别为 1, 4, 1,取最大值 4,所以这个结构体的大小必须是 4 的整数倍,我们已经使用了 9 个字节了,那 4 的最小整数倍就是 12,所以结构体 S1 的总大小为 12
四.练习题
这里博主留下俩道练习题,有兴趣的朋友们可以自己尝试算一算俩个结构体分别占多大的字节空间,对于题目的见解,也欢迎各位将自己的思路和答案打在评论区内,大家一起讨论学习
struct S3 { double d; char c; int i; };
struct S4 { char c1; struct S3 s3; double d; };
本次的分享就到此为止了,如果你有不同的见解,欢迎在评论区相互交流,感谢您的支持