目录
1.什么是内存对齐
带着这个问题我们先看一个代码
struct book { char num; char name; int age; }s1; struct book1 { char num; int age; char name; }s2; int main() { printf("%d\n", sizeof(s1)); printf("%d\n", sizeof(s2)); return 0; }
可以看到,同样的成员结构体所占内存的空间确实不同的,这就涉及到了内存对齐。所谓内存对齐,就是将数据存放到一个是字的整数倍的地址指向的内存之中。cpu就可以通过这些地址在内存中访问这些数据。32(64)位的cpu一次性访问计算4(8)个字节的数据。内存对齐的本质是以空间换时间,按照内存对齐后cpu访问效率则提高,且可移植性增强。
2.offsetof的使用
介绍如何内存对齐之前需要明白offsetof宏的使用,offsetof是计算联合类型成员的地址据该类型起始地址的远近(单位为字节)的函数
图中所示结构体s1中有偏移量为2和3的地址处无数据,这就由于内存对齐。下面讲述内存对齐的规则。
3.如何内存对齐
1.结构体的第一个成员存放在结构体变量开始位置的0偏移量处。
2.从第二个成员开始,都要对齐到对齐数的整数倍的地址处(对齐数:成员自身大小和默认对齐数两者的较小值,其中vs环境下默认对齐数是8,linux没有默认对齐数,其对齐数为成员自身大小)。(若成员是数组,对齐数也是数组的类型与默认对齐数的最小值)
3.结构体的总大小是该结构体最大对齐数(成员对齐数的最大值)的整数倍。(包括嵌套结构体的对齐数)
4.如果是嵌套结构体,嵌套在里面的结构体对齐到自身最大对齐数的整数倍。
举个例子
先算s2 的大小,num为第一个成员位于偏移量0处,第二个成员int对齐数为4,故需要储存在偏移量为4的整数倍处即4-7,name的对齐数1的整数倍是任意数,储存在偏移量为8处,由第三条规则结构体总大小是最大对齐数的整数倍,4的整数倍12,故补齐到11,0~11共12个字节。
再算s1的大小(添加s2后的),s2最大对齐数4,刚好s2存于对齐数为8处(s2大小为12),且总大小为最大对齐数4的整数倍,4*5 = 20(0~19)。故s1的大小为20.
4.为什么要有内存对齐
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。(32(64)位的cpu一次性访问计算4(8)个字节的数据。内存对齐的本质是以空间换时间,按照内存对齐后cpu访问效率则提高,且可移植性增强。)