全网最细结构体的内存对齐讲解


前言 

相信各位道友在刷题或者在笔试面试中经常会遇见求结构体内存大小的问题,这个问题的核心就在于结构体的内存对齐。因此,让我带领大家三分钟通关结构体的内存对齐。


一、内存对齐的细则

1.结构体的第一个成员在与结构体变量偏移量为0的地址处。

2.结构体中的其他成员要对齐到对齐数的整数倍的地址处(对齐数为编译器默认对齐数和该成员大小的较小值)。

3.结构体的总内存大小为最大对齐数(即每个成员的对齐数中的最大值)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

二、内存对齐的示例和分析

编译器环境

这四个实例均是在VS2019的64位操作系统下测试的,VS2019的默认对齐数为8。

提醒:若是在别的编译器环境下测试,请根据编译器的默认对齐数进行调整

实例1

代码如下:

//练习1
struct S1
{
	char c1;
	int i;
	char c2;
};

该结构体为12个字节

内存布局如下:

 

内存布局解析如下:

结构体的第一个成员在与结构体变量偏移量为0的地址处,因此c1的内存分布如图,而对于int 类型的i,因为在64位的环境下,int类型的数据占4个字节,而VS2019的默认对齐数为8个字节,取较小值为对齐数,因此对齐数为4,又因为c1在偏移量为0的地址处,故 i 只能存到偏移量为4的地址处,中间浪费了三个字节的空间,对于char 类型的 c2,其内存大小为1个字节,而VS2019的默认对齐数为8个字节,取较小值为对齐数,因此对齐数为1,因此 c2 存在偏移量为9的地址处。最后一步,.结构体的总内存大小为最大对齐数(即每个成员的对齐数中的最大值)的整数倍,因为c1的对齐数为1,i 的对齐数为4,c2的对齐数为1,故最大对齐数为4,而此时结构体最后一个成员在偏移量为9的地址处,这不符合,因此需要再浪费3个字节的内存,最终结构体内存大小为12个字节。符合内存对齐的四个规则。

实例2

代码如下:

//练习2
struct S2
{
	char c1;
	char c2;
	int i;
};

该结构体为8个字节

内存布局如下: 

内存布局解析如下: 

结构体的第一个成员在与结构体变量偏移量为0的地址处,因此c1的内存分布如图,而c2为1个字节,VS2019默认对齐数为8个字节,取较小值为对齐数,因此c2的对齐数为1,则c2在偏移量为1的地址处,而对于 i ,i 为int 类型占4个字节,VS2019默认对齐数为8个字节,取较小值为对齐数,因此 i 的对齐数为4,因此 i 在偏移量为4的位置处,这中间浪费两个字节。最后一步,结构体的总内存大小为最大对齐数(即每个成员的对齐数中的最大值)的整数倍,因为c1和c2的对齐数均为1,i 的对齐数为4,因此最大对齐数为4,但因为此时结构体大小为8,正好为4的整数倍,因此不需要调整,故内存分布如上。

实例3

代码如下: 

//练习3
struct S3
{
	double d;
	char c;
	int i;
};

 该结构体为16个字节

内存布局如下:

内存布局解析如下: 

结构体的第一个成员在与结构体变量偏移量为0的地址处,而又因为d为double类型,因此其占用8个字节,因此d的分布如图,而c为char类型,大小为1个字节,小于vs2019的默认对齐数8,因此其对齐数为1,因此c在偏移量为8的地址处,对于int 类型的 i ,其对齐数为4,但因为偏移量为8的内存空间已经被占用,因此只能浪费3个字节,再偏移量为12的地址处。最后一步,结构体的总内存大小为最大对齐数(即每个成员的对齐数中的最大值)的整数倍,而d的对齐数为8,c的对齐数为1,i 的对齐数为4,因此最大对齐数为8,但因为上述分析最后结构体大小为16个字节,正好为8的倍数,因此不需要调整,故内存分布如上图。

实例4——结构体嵌套

代码如下: 

struct S3
{
	double d;
	char c;
	int i;
};

//练习4---结构体嵌套问题
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

 内存布局如下:

内存布局解析如下: 

结构体的第一个成员在与结构体变量偏移量为0的地址处,而c1为char类型,占用1个字节的空间,因此c1的分布如上图,而对于s3,它的类型为struct S3,再第三个题的分析中,它占用16个字节的空间,且它的最大对齐数为8,因此s3的偏移量为8的倍数,因此它的内存分布如上图,而对于double类型的d,大小为8个字节,而VS2019的默认对齐数为8,因此d的对齐数为8,因此d的偏移量应为8的倍数,因此d再偏移量为24的位置上。最后一步,结构体的总内存大小为最大对齐数(即每个成员的对齐数中的最大值)的整数倍,而c1的为1,s1的为8,d的为8,而上述分析下结构体大小为32个字节,正好为8的倍数,因此不需要调整,故分布如上图。

 三、内存对齐的意义

平台原因(移植原因)

1.不是所有的硬件平台都能访问任意地址上的任意数据的

2.某些硬件平台只能在某些地址处去某些特性类型的数据,否则会抛出硬件异常,而结构体内存对齐可以避免上述问题

性能原因

数据结构(尤其是栈)应尽可能地再自然边界上对齐

因为对于那些未对齐的内存,处理器需要作两次内存访问,而对于对齐的内存,只需要一次访问即可得到数据。

总的来说,结构体的内存对齐就是拿空间来换取处理器处理的时间。


总结

本篇博客我们一起回顾了内存对齐的细则(这个是c语言极其重要的知识点),而肯定有道友会想到编译器的默认对齐数可不可以修改,答案肯定是可以的,因此希望各位道友点点关注,博主的下一篇博客会带领各位道友修改默认对齐数,减少结构体浪费的空间。

谢谢!!!

猜你喜欢

转载自blog.csdn.net/m0_70322372/article/details/127274027