什么是内存对齐

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

为什么要内存对齐

平台原因不是所有的CPU都能访问任意地址上的任意数据的,有些CPU只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

性能原因数据结构应该尽可能地在自然边界上对齐,如果为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。CPU处理器把内存当作一块一块去读取,块的大小可以是2、4、8、16字节大小,这个大小称为内存存取粒度。假设当前处理器的内存存取粒度为4,对于一个int变量(大小4字节),分两种情况讨论:

数据从第0字节开始存放(已内存对齐)CPU只需要访存一次,就可以把4字节数据读完,然后存入寄存器。

数据从第1字节开始存放(没有内存对齐)数据不处于自然边界上,CPU需要分两次访存,第一次先访问[0, 3]字节进入寄存器,第二次访问[4, 7]字节进入寄存器,然后剔除第0、5、6、7字节,仅留第1、2、3、4字节数据进入寄存器。对于未内存对齐的数据,显然大大降低了CPU的处理性能。这种未对齐的情况有些CPU甚至直接开摆。

//#pragma pack(n) 设置编译器对齐系数,msvc 编译器默认为8byte

内存对齐规则

假设在一个结构体中,最大数据类型长度为m,编译器对齐系数是n,则min(m, n)叫做对齐单位s。所以当设置的对齐系数n大于类中最大数据类型长度,该设置是不起作用的。当n等于1时,整个类的大小为所有成员长度之和。

了解了以上概念后,我们来看具体的内存对齐规则:

每个成员的对齐规则:

类中第一个成员的偏移量(offset)为0,以后每个成员(该成员的数据类型长度为k)相对于结构体首地址的offset为min(k, s)的整数倍。

如果一个类里有结构体成员,则结构体成员要从其内部最宽基本类型成员的整数倍地址开始存储。

整体对齐规则:

整个结构体的大小应是对齐单位s的整数倍。

引用:C++内存对齐

结论

类中成员按照从大到小声明,整体内存小。