字节对齐 |
为了能使CPU对变量进行高效快速的访问,变量的起始地址应该具有某些特性,
char short (16-bit) int and long (32-bit) float double
structures X = min(n, sizeof(item)) 例如,对于结构体 struct {char a; int b} T; 当位于32位系统,n=8时: 当位于32位系统,n=2时: 结构体的sizeof 例如:32位系统,n=8, 注意: 1) 对于空结构体,sizeof == 1;因为必须保证结构体的每一个实例在内存中都 2) 结构体的静态成员不对结构体的大小产生影响,因为静态变量的存储位置与 例如: struct {static int I;} T; struct {char a; static int I;} T1; 3) 某些编译器支持扩展指令设置变量或结构的对齐方式,如VC,
并不是要求#pragma pack(8),就一定是每个成员都是8字节对齐 struct s2 |
其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;例如上面第二个结构体变量的地址空间。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
需要字节对齐有设计者的考虑,原来这样有助于加快计算机的存取速度,否则就得多花指令周期了。所以,编译器通常都会对结构体进行处理,让宽度为2的基本数据类型(short等)
都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上。正是因为如此两个数中间就可能需要加入填充字节,所以结构体占的内存空间就增长了。
为什么会有内存对齐
以下内容节选自《Intel Architecture 32 Manual》。
字,双字,和四字在自然边界上不需要在内存中对齐。(对字,双字,和四字来说,自然边界分别是偶数地址,可以被4整除的地址,和可以被8整除的地址。)
无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。
某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常(#GP)。双四字的自然边界是能够被16整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。