作者:胖小夜曲
链接:https://www.jianshu.com/p/4b36f865adb9
来源:简书
目录
一、结构体对齐的三大原则
二、三道例题
三、为什么要内存对齐
1、数据成员对齐规则:结构(struct)(或联合体(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组、结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)
2、结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里存有struct b,b里面有char、int 、double等元素,那b应该从8的整数倍开始存储)
3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
首先,我们看一下各个数据类型在不同操作系统中所占的位数
图中输出结果是24,为什么是24呢?
a 为double类型,占8个字节,又因为是第一个成员,起始值对应offset为0的位置。又因为占8个字节,所以a对应的是偏移量为8的地址空间
b 为char类型,占1个字节,对齐到1的整数倍,也就是下一个地址空间。
c 为int类型,占4个字节,对齐到4的整数倍,对齐数为12,偏移量为4的地址空间
d 为short类型,占2个字节,对齐到2的整数倍,对齐数为16,偏移量为2的地址空间
结构体的总大小,必须是其内部最大成员的整数倍,不足的要补齐,struct1中最大的成员为double类型,那么结构体的总大小即为8的整数倍,目前已经占了17字节,所以总大小应为24个字节
我们再看一下下面的输出又是多少呢?
输出结果是16。
如图a不变
b 为int类型,占4个字节,对齐到4的整数倍,对齐数为8,偏移量为4的地址空间
c 为char类型,占1个字节,对齐到1的整数倍,也就是下一个地址空间。
d 为short类型,占2个字节,对齐到2的整数倍,对齐数为14,偏移量为2的地址空间
又因为内存对齐原则,所以结构体的总大小为16个字节
那么结构体是否可以嵌套使用呢,嵌套使用的话,内存又该如何计算呢?
这里的输出结果是多少呢。我们来计算一下
如图a不变
b 为int类型,占4个字节,对齐到4的整数倍,对齐数为8,偏移量为4的地址空间
c 为char类型,占1个字节,对齐到1的整数倍,也就是下一个地址空间。
d 为short类型,占2个字节,对齐到2的整数倍,对齐数为14,偏移量为2的地址空间
e 为int类型,占4个字节,对齐到4的整数倍,对齐数为16,偏移量为4的地址空间
已知struct1占24个字节,double 占8个字节,不知道为什么的请往上看。
结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里存有struct b,b里面有char、int 、double等元素,那b应该从8的整数倍开始存储)
所以struct1 应该从8的整数倍开始存储
那么struct1 占24个字节,对齐到8的整数倍,对齐数为24,偏移量为24的地址空间
所以结构体的总大小为48个字节
(一)性能上的提升
从内存占用的角度讲,对齐后比未对齐有些情况反而增加了内存分配的开支,是为了什么呢?
数据结构(尤其是栈)应该尽可能地在自然边界上对齐,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。最重要的是提高内存系统的性能。
(二)跨平台
有些硬件平台并不能访问任意地址上的任意数据的,只能处理特定类型的数据,否则会导致硬件层级的错误。
有些CPU(如基于 Alpha,IA-64,MIPS,和 SuperH 体系的)拒绝读取未对齐数据。当一个程序要求这些 CPU 读取未对齐数据时,这时 CPU 会进入异常处理状态并且通知程序不能继续执行。
举个例子,在 ARM,MIPS,和 SH 硬件平台上,当操作系统被要求存取一个未对齐数据时会默认给应用程序抛出硬件异常。所以,如果编译器不进行内存对齐,那在很多平台的上的开发将难以进行。