目录
内存对齐
1. 为什么要进行内存对齐
- 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
- 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。 CPU 并不是以字节为单位存取数据的,cpu把内存当成是一块一块的,块的大小可以是2,4,8,16 个字节,因此CPU在读取内存的时候是一块一块进行读取的,块的大小称为(memory granularity)内存读取粒度。
我们再来看看为什么内存不对齐会影响读取速度?
假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4),分两种情况讨论:
第一种情况:数据从0字节开始。当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,结算完成了。
第二种情况:数据从1字节开始。当数据从1字节开始的时候,问题很复杂,首先先将前4个字节读到寄存器,并再次读取4-7字节的数据进寄存器,接着把0字节,4,6,7字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器,对一个内存未对齐的寄存器进行了这么多额外操作,大大降低了CPU的性能。
2. 结构体内存对齐原则
2.1在有#pragma pack宏的情况下
结构体的自身宽度就是宏上规定的数值大小,所有内存都按照这个宽度去布局
#pragma pack 参数只能是 ‘1’, ‘2’, ‘4’, ‘8’, ‘16’
2.2在没有#pragma pack宏的情况下
结构体的自身宽度有最大成员属性的宽度决定。
1、第一个成员的首地址为0.
2、每个成员的首地址是自身大小的整数倍; 如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
3、结构体的总大小,为其成员中所含最大元素类型的整数倍。
struct Outside {
//(7):根据第3条,最大元素类型为long double 16字节,a + b +c = 7不是16的整数位,补齐为16,可知Outside大小16+32=48字节
char a; //(1):第一个成员,首地址为0字节位,char占1个字节
int b; //(2):int为4个字节,根据第2条原则,首地址为4
short c; //(3):short为2个字节,首地址为8
struct Inside {
//(6):根据第2条,首地址为16,根据第3条,可知Inside大小为32字节
int a; //(4):int为4个字节,首地址为inside结构体的0字节位
long double b; //(5):long double为16字节,根据第2条原则,首地址为inside结构体的第16字节位
} inside;
}