为什么计算机系统要进行数据对齐?
数据对齐简化了处理器和内存系统之间的接口设计,并提高了内存系统的性能和效率。例如,假设一个cpu总是从内存中取8个字节,则地址必须为8的倍数。如果能够保证double类型数据的地址对齐为8的倍数,那么就可以只用一个内存操作来对数据进行读写。否则,可能需要执行两次内存访问,因为地址没有对齐为8的倍数,该对象可能被分放在两个8字节内存块中。
数据对齐的原则:任何K字节的基本对象的地址必须是K的倍数。
类型 | K Byte |
---|---|
char | 1 |
short | 2 |
int, float | 4 |
long, double, char* | 8 |
来看几个例子,比如考虑下面的结构体声明:
struct S1 {
int i;
char c;
int j;
};
没有采用数据对齐,编译器采用最小的9字节分配,画出来的图是这样的:
这不满足i(偏移为0)和j(偏移为5)的4字节对齐要求。数据对齐之后则是:
c和j之间的蓝色部分则是插入的3字节的间隙,此时j的偏移为8,整个结构的大小为12字节。
考虑下面这个结构体:
struct S2 {
int i;
int j;
int c;
};
如果将struct S2打包成9个字节,只要保证了结构的起始地址满足4字节对齐的要求,那么就能够保证字段i和j的对齐要求。
考虑下面的声明:
struct S2 d[4];
分配9个字节,不可能满足d的每个元素的对齐要求,因为此时这些元素的起始地址分别为xd、xd+9、xd+18、xd+27。要想d的元素地址都满足4的倍数,就需要在结构体的末尾填充3个字节。
此时这4个元素的起始地址分别为xd、xd+12、xd+24、xd+36,满足对齐要求。
练习:
针对下面的结构声明,确定每个字段的偏移量和结构体的大小,以及在64位系统上的对齐要求:
A. struct P1 { int i; char c; int j; char d; };
B. struct P2 { int i; char c; char d; long j; };
C. struct P3 { short w[3]; char c[3] };
D. struct P4 { short w[5]; char *c[3] };
E. struct P5 { struct P3 a[2]; struct P2 t };
A.
B.
C.
D.
E.
ps:上图中蓝色部分表示因为数据对齐而填充的数据。
要想在结构体中最小化浪费空间,一种有效的策略是按照大小的降序排列结构的元素。