强制对齐的
对于大多数IA32指令来说,保持数据对齐能够提高效率,但是它不会影响程序的行为。另一方面,如果数据未对齐,有些实现多媒体操作的SSE指令就无法正确地工作。这些指令对16字节数据块进行操作,在SSE单元和存储器之间传送数据的指令要求存储器地址必须是16的倍数。任何试图以不满足对齐要求的地址来访问存储器都会导致异常(exception),默认的行为是终止程序。
因此IA32的一个惯例是,确保每个栈帧的长度都是16的整数倍。编译器就可以在栈帧中以每块的存储都是16字节对齐的方式来分配存储。
Microsoft Windows的对齐
Microsoft Windows对齐的要求更严格------任何K字节基本对象的地址都必须是K的倍数,K=2,4或者8。特别地,他要求一个double 或者long long 类型数据的地址应该是8的倍数。这种要求提高了存储器的性能,而代价则是浪费了一些空间。linux的惯例是8字节数据在4字节边界上对齐,这可能对i386很好,因为过去存储器十分缺乏,而存储器接口只有4字节宽。对于现代处理器来说,Microsoft的对齐策略就是更好的选择。在windows和linux上,数据类型long double都有4字节对齐的要求,为此GCC产生的IA32代码分配12个字节(虽然实际的数据类型只需10个字节)。
Microsoft Windows下结构体对齐一般而言应满足一下3个准则;
Microsoft Windows下任何K字节基本对象的地址都必须是K的倍数,K=2,4或者8。
1 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2 结构体每个成员相对于结构首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间填充字节(internal adding);
3 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员后面加上填充字节(trailing adding);
对于上面的准则,有2点需要说明:
1 结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获取,这个宏在stddef.h中定义如下:
#define offsetof(s,m) (size_t)&(((s *)0)->m)
2 基本类型是指如char,short,int,float,double这样的内置类型,这里所说的“数据宽度“是指sizeof的大小。由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员是,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体来看待。
LINUX下结构体对齐和Microsoft Windows的不同
linux下任何K字节基本对象的地址都必须是K的倍数,K=2或者4。由于linux的惯例是8字节数据在4字节边界上对齐,所以linux下的准则和Microsoft Windows下有所不同:
1 如果结构体的最宽基本类型成员不为8,则结构体变量的首地址能够被其最宽基本类型成员的大小所整除
2 如果结构体的最宽基本类型成员为8,则结构体变量的首地址能够被4整除
3 结构体每个成员相对于结构首地址的偏移量(offset)都是成员大小的整数倍(除8字节数据类型外),8字节数据类型的成员以4字节对齐
4 结构体的总大小为4的整数倍
EXAMPLE:
对于结构体声明
struct {
char a;
short b;
double c;
char *d;
}foo;
A 这个结构体中所有字段的字节偏移量是多少?
B 这个结构体总的大小是多少?
在考虑这两个问题时需要对不同的操作系统进行分析
在Windows机器上编译它:
A 这里是对象大小和字节偏移量
字段 | a | b | c | d |
大小 | 1 | 2 | 8 | 4 |
偏移量 | 0 | 2 | 8 | 16 |
在Linux上编译它:
A 这里是对象大小和字节偏移量
字段 | a | b | c | d |
大小 | 1 | 2 | 8 | 4 |
偏移量 | 0 | 2 | 4 | 12 |
X86-64中的数据结构遵循的原则:
X86-64遵循的原则与IA32一样:数组是作为同样大小的块的序列来分配,这块中保存这数组元素;结构体则作为变长的块来分配,块中保存这街头体中的元素;联合体是作为一个单独的块来分配,这个块足够大,能够装下联合体中最大的元素。
区别是X86-64遵循严格的对齐要求。对于任何需要K字节的标量数据类型来说,它的起始地址必须是K的倍数。因此数据类型long 和double以及指针,都必须在8字节边界上对齐。此外,数据类型long double使用16字节对齐(分配也是16个字节大小),虽然实际表示只需10个字节。强加上这些对齐是为了提高存储器系统性能-----最新的处理器中,存储器接口被设计成读或写对齐的块,这些块是8或16字节长。