struct的内存对齐

由布多(budo)发布于 2022年7月9日 • 最后更新:2023年11月1日

运行环境:macOS 14.0
编译工具:Xcode 15.0

关于 struct 内存对齐的文章网上已经有很多了,但大部分文章都讲的不清不楚;该文章只是一份笔记,用于记录我学习 struct 内存对齐的过程。

虽然原理很简单,但为了讲清楚,还是花了我一天的时间,真的大大超乎我的意料。

关于什么是内存对齐?以及为什么要内存对齐,这里不再重复,网上有很多资料,请自行搜索。

该文章以 struct 为基础。内存对齐分为内对齐和外对齐。先将 struct 中所有元素进行内对齐,最后再对 struct 进行外对齐。

什么是内存对齐

计算机的内存空间都是按照字节划分的,从理论上讲似乎对任何类型的变量都可以从任何地址开始,但实际上计算机系统对基本类型数据在内存中存放的位置是有限制的,它们会要求这些数据的首地址是某个数k(64位环境下通常是8)的倍数,这就是所谓的内存对齐。

为什么要进行内存对齐

简单的说就是有空间换时间。
尽管内存是以字节为单位,但是大部分处理器并不是按照一个字节一个字节来存取内存的,它一般会以双字节、4字节、8字节、16字节甚至32字节为单位进行存取内存,我们将上述这些存取单位称为内存存取粒度。

假设现在有一台 64 位系统的处理器,该处理器只能从地址为8的倍数的内存开始读取数据。

假如没有内存对齐机制,数据可以任意存放,现在有一个 double 变量存放从地址1开始的连续8个字节中,该处理器去取数据时,要先从0地址开始读取第一个8字节块,剔除不想要的字节(0字节),然后从地址8开始读取下一个8字节块,同样剔除不要的数据(9…15),最后留下的两块数据合并放入寄存器,这需要做很多工作。

简单的说内存对齐能够提高 cpu 读取数据的速度,减少 cpu 访问数据的出错性。

现在有了内存对齐,double 类型数据只能存放在按照对齐规则的内存中,比如说0地址开始的内存,那么现在该处理器在取数据时一次性就能将数据读出来了,而且不需要额外的操作,提高了效率。

为什么必须是8的倍数?因为64位处理器一次最多可以处理8个字节的数据,如果读取少于8个字节的数据,那么就浪费,多了也没有用。这样可以以最快的速度寻址,不遗漏一个字节,也不重复对一个字节寻址。

内存对齐规则

每个特定平台上的编译器都有自己的默认“对齐系数(对齐模数)”,64 位系统中默认是 8,可以通过 #pragma pack(n) 来改变,其中n可以是1、2、4、8、16。

对齐单位(有效对齐值):是给定值 #pragma pack(n) 和结构体中最长数据类型长度较小的那个。

了解了上面的概念后,我们现在可以来看看内存对齐需要遵循的2条规则:

  1. 内对齐规则:结构体第一个成员的偏移量为0,以后每个成员相对于结构体首地址的偏移量都是该成员大小与有效对齐值较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。
  2. 外对齐规则:结构体的总大小为有效对齐的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
文章作者: 布多
文章链接: https://internetwei.github.io/2022/07/09/一文读懂内存对齐/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 布多的博客