数据对齐是计算机系统对数据在内存中存放位置的限制,所以数据对齐被称为内存对齐。数据对齐要求某些类型的对象存放的地址为2n(n=1,2,3)的倍数,也就是说对象的地址最低位为0或者最低两位为0等。
一、为什么数据对齐
数据对齐限制了对象在内存中的存放位置,数据对齐的意义在于提高了数据访问的效率。例如,一个处理器一次能够从存储器中读取4字节的数据,如果存放的地址是4的倍数,则处理器只要访问存储器一次就可以完成数据的读取;如果存储地址不是4的倍数,处理器要访问存储器两次来完成数据的读取。
下图是前半部分是进行了对齐,后半部分是未进行对齐的一种可能情况。为进行对齐需要处理器读取两次,第一次是读取地址为0~3的部分数据,第二次读取地址为4~7的部分数据。
数据对齐可能会浪费一部分内存空间。如下图所示,偏移量为0的位置存放了4字节的对象i,这4字节之后存着1字节的对象c,要在c之后存放4字节的对象i,且要满足4字节对齐的原则,那么j的偏移量应为8。然而偏移为5~7的地址空间没有存储任何信息。这造成了一定的空间浪费。
所以,数据对齐和算法中的空间换时间的思想是一样的。
二、数据对齐的规则
对于基本数据类型,计算机系统对不同的类型有不同的默认对齐参数。Linux中采用的对齐规则是2字节的数据类型(如short)的地址必须是2的倍数,更多字节的数据类型(如int、int*、double)的地址必须是4的倍数; Windows采用的对齐规则是K字节的数据类型的地址必须是K的倍数,2字节的对象的地址是2的倍数,4字节的对象如int、float的地址必须是4的倍数,与Linux不同之处在于8字节的对象的地址必须是8的倍数。
显然Windows采用的对齐规则会浪费更多的存储空间,但现代计算机的内存空间变得越来越便宜,Windows所采用的对齐规则是一个很不错的选择。
而对于自定义的数据类型,例如结构体这样的数据类型,在存储器中的存放为了符合数据对齐的要求,各个成员对象存储的地址很可能不是连续的。为了满足对齐的要求,成员对象的存储空间后面有可能需要插入一段空白的空间。
例如下面的结构体,struct1的b成员为了满足4字节对齐的规则,不能直接直接存储在char类型的a之后,所以在成员a之后有3字节的空白。1
2
3
4
5
6
7
8
9
10
11
12
13struct struct1
{
char a;
int b;
short c;
}
struct struct2
{
int b;
char a;
short c;
}
此外,对于结构体这样的数据类型,还有一条对齐规则就是整个对象的大小必须是最大成员所占字节的整数倍。所以,还以上面的两个结构体为例,在32位字长的机器中,struct1的所占大小不是4+4+2=10字节,而是12字节。但struct2所占的大小确实8字节,由此可见,结构体成员定义的顺序不同会导致结构体所占大小也不同。