0
点赞
收藏
分享

微信扫一扫

struct内存对齐

weednoah 2022-04-25 阅读 58
c++

结构体内存对齐

对齐原则

在明白了为何要进行内存对齐之后,我们来分析结构体内的内存对齐,在进行具体的实例分析前,需要给出结构体内存对齐的两条基本原则。

一、结构体各成员变量的内存空间的首地址必须是“对齐系数”和“变量实际长度”中较小者的整数倍。

二、对于结构体来说,在其各个数据都对齐之后,结构体本身也需要对齐,即结构体占用的总大小应该为“对齐系数”和“最大数据长度”中较小值的整数倍。

在给定了基本原则之后,我们通过一个例子来说明结构体的内存对齐,假定当前的处理器是 32 位的,对齐系数为4。在这里笔者选择在上一篇文章中涉及到的一个结构体进行解析,结构体如下:
在这里插入图片描述
根据我们刚刚给出的第一条对齐原则,先确定出每个变量的存储位置,变量存储方式是小端对齐,为了看起来更加形象,以 16 个字节作为一行来表示变量的存储位置(这里所说的存储位置是指相对于结构体起始地址的偏移)。
在这里插入图片描述
根据第一条规则:各成员的内存空间的首地址必须是对齐系数和变量本身大小较小者的整数倍,这里对齐系数是 4,因此变量 a 、数组 c 、变量 e 、变量 g 的首地址需要满足 1 的倍数,变量 b 的首地址需要满足 2 的倍数,变量 d 的首地址需要满足 4 的倍数,变量 f 的首地址需要 4 的倍数。所以也就有了上述表格中的变量存储位置。那既然结构体内的成员都已经对齐了,为什么还存在第二条原则呢?也就是说为什么结构体内的成员已经内存对齐了,结构体本身还需要对齐?下面通过一个结构体数组来说明,比如我们定义了这样一个结构体数组:
在这里插入图片描述
我们在放置成员存储位置的时候,data[0] 按照成员对齐的原则依次存放,放到最后一个结构体成员时,如果不考虑结构体本身的对齐,按照数组元素是紧挨着存放的原则,那这个结构体数组应该是按照下图进行存储的:
在这里插入图片描述
从上图中我们可以看到虽然 data[0] 中的成员都对齐了,但是由于结构体本身的不对齐,导致 data[1] 中的好多成员都不对齐了,因此,在完成了结构体成员的内存对齐后,我们还需要依据第二条原则:结构体占用的总大小应该为“对齐系数”和“最大数据长度”中较小值的整数倍,来对结构体本身进行对齐,因此正确的结构体数组的存储位置应该如下图所示:
在这里插入图片描述
这里需要注意的是,上述原则针对的是结构体占用的总大小,而不是结构体的首地址,所以,在结构体本身还没有对齐的情况下,data[0] 的大小是 25 个字节,但是根据上述原则,在对齐系数为 4 的前提下,结构体大小应该是 4 的整数倍,所以要对结构体进行所占内存进行填充,因此:data[0] 最终的大小是 28 字节,结构体数组 data 的大小为 56 字节

结构体内成员顺序

通过上述分析我们可以很容易就想到,根据第一条原则,那么结构体成员定义的先后顺序会对最终结构体占用的内存造成影响,比如现在调整结构体 data 内成员的定义顺序,如下:
在这里插入图片描述
改变结构体成员顺序后的存储位置如下:
在这里插入图片描述
通过图片就可以看到只有一块蓝色的填充区域,在成员对齐之后,结构体大小是 20 ,已经是 4 的整数倍,已经无须再做填充,所以调整顺序后的结构体大小为 20 个字节,相比于之前没有改变顺序之前整整减少了 8 个字节,也可以看出结构体成员的定义顺序也是需要关注的一个问题,关于结构体内成员定义的顺序应该遵循这样一个原则:按照长度递增的顺序依次定义各个成员。

如何设定对齐系数

查看默认对齐系数

在上述我们对结构体内存对齐的分析中,我们都是假定对齐系数为 4 ,实际上对于编译器来说都有默认的对齐系数,我们可以输入伪指令,然后以报警信息的方式显示当前的对齐系数:

<span style="color:#000000"><span style="background-color:#282c34"><code class="language-cpp"><span style="color:#98c379">#<span style="color:#c678dd">pragma</span> pack(show)</span>
</code></span></span>
  • 1

设置对齐系数


在这里,设置1字节对齐其实也就相当于不进行内存对齐,因为任何地址都可以是 1 的整数倍,最后,需要注意的是使用这种方法设置字节对齐,要在想要取消一字节对齐的地方使用伪指令 #pragma pack() 取消一字节对齐,否则后面所定义的结构体会继续采用刚刚所设置的对齐方式。除了采用上述这样设置一字节对齐的方式取消内存对齐,也可以采用下面的方式取消字节对齐:
在这里插入图片描述
这种方式相对于上述方法来讲,不用执行取消操作,作用域只是本结构体,不会影响其他结构体的对齐方式。最后,取消字节对齐的结构体(或者说是按照 1 字节对齐的结构体)data 的大小就是 19 个字节,即将结构体内的所有成员的字节大小相加即可。

总结

了解结构体的内存对齐,从而在定义结构体成员时按照最优的顺序进行定义,对于 RAM 资源比较紧缺的 MCU 来讲,也是非常重要的。

举报

相关推荐

0 条评论