如何计算结构体的字节大小?结构体内存对齐详解
前言:在笔者看来,C/C++相对于其他高级语言来说,最传神的一点就是对于内存空间的精准分配,C/C++给予了程序员们很大的权力去手动管理内存。对于内存进行更好的管理可以从本质上提高我们程序的运行效率,而管理内存的前提条件则需要我们深刻的理解内存的分布,在阅读完本篇文章后,相信你一定能够更好的理解C/C++中内存分布的奥妙。
一:内存对齐的引入
我们先看一组C/C++代码并且思考思考会输出什么:
//结构体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));
我们可以看到俩个结构体的成员类型都是一模一样的:
- c1 都是 char 类型
- c2 都是 char 类型
- i 都是int 类型
唯独不同的是成员直接的相互顺序不同,按道理来说应该输出的是同一个值,那结果真是这样吗,我们继续往下走
令人意外的一点是,我们输出的结果完全不一样,一个是 8 一个是 12 ,出现这样的结果是为什么呢?为什么结构体之间成员的顺序不同就会导致结构体的大小不同呢?这中间差的 4 又是差在哪呢?
为了解决上述的问题,就需要介绍到“结构体内存对齐”的概念了
二.内存对齐的探究
为了更加方便我们观察结构体内部的细节,我们需要使用到 offsetof宏 来进行观察
我们可以打开cpp官网:offsetof - C++ Reference (cplusplus.com)
通过查询资料得知,offsetof 是用来计算结构体中成员单位相较于起始位置的偏移量的一个宏,具体使用如下
//查看S1的成员的偏移量
printf("%zd\n", offsetof(struct S1, c1));
printf("%zd\n", offsetof(struct S1, i));
printf("%zd\n", offsetof(struct S1, c2));
我们可以根据上述的输出结构画出对应的内存结构图:
我们使用同样的方法处理结构体2
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
以上就是对于结构体内存对齐的具体计算
四.Test
这里笔者给出俩个结构体,大家可以自己进行计算
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
本次分享就到此为止了
- 点赞
- 收藏
- 关注作者
评论(0)