x86机器对于word或double word的存储时,会将低byte存在低地址,高byte存于高地址。
byte,word和double word可以开始于任何地址,并可以重叠。
对于数据总线是8bit的机器,不存在内存对齐的问题。
对于数据总线是16bit的机器,内存单元被分为even address 和odd address,cpu寻址时,都是以even address来作为计数的,每次都读出2 bytes。even address上的0~7bit对应于低字节,odd address上的8~15bit对应于高字节。此时,默认对齐大小为2。当一个word是以odd address开始存储的,那么一个内存周期即可将它读出。但若是odd address存储的,例如占的是101和102字节,则在第一个内存周期内,cpu从内存中读出100和101字节,并将101字节存于数所总线8~15的高8bit上。第二个时钟周期时,再从102开始读取2bytes,同时102字节上的8bit存于数据总线0~7的低8位上。cpu会自动交换数据总线上的高低字节,从而得到正确的word数据。
对于byte,如果是以2字节对齐,则直接读取,从数据总线的0~7bit上得到数据。如果不是以2字节对齐,cpu将按字节地址减1来寻址,并将从数据总线8~15bit上的数据自动交换到0~7bit上。
对于double word,当以2字节对齐时,两个内存周期内可以完成读取。如果不是以2字节对齐,则需要3个内存周期。
对于数据总线为32bit的机器,数据总线和内存如图1所示:
原理与数据总线是16bit和8bit的情况类似。
具体总参见:http://my.unix-center.net/~Simon_fu/?p=262
再来看看内存对齐。
需要进行内存对齐的原因有两个:
1.各个硬件平台对存储空的处理上有很大的不同,一些平台对某些特定类型的数据只能从某些特定地址开始存取。对内对齐是为了保证可移植性。
2.如果不按适合平台的要求对数据存放进行对齐,会在存取数据上带来效率上的损失。
对齐的原则:
1.数据类型自身的对齐值:
char 为1,short为2,int,float,double为4等。
2.结构体或者类的自身对齐值:
其成员中自身对齐值最大的那个值。
3.指定对齐值:
#pragma pack(value)时的指定对齐值(vlaue);
#pragma pack () 取消自定义对齐方式;
__attribute ( (aligned (n))) 所作用的结构成员对齐在n字节自然边界上,如果结构中有成员的长度大于n,则按照最大成员长度来对齐;
__attibute__((packed))取消结构在编译过程的优化对齐,按照实际占用字节数对齐。
4.数据成员、类或结构体的有效对齐值:
自身对齐值与指定对齐值中小的那个。
有效对齐值N最终用来决定数据存放地址方式的值,有效对齐值N,就是表示对齐在N上。也就是说,数据的存放起始地址%N=0。
结构体中的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整。
举个例子:
struct B {
char b;
int a;
short c;
}
假设B从地址空间0x0000开始排列,系统默认对齐值是4。b的对齐值为1,比指定或默认对齐值小,所以其有效对齐值为1,存放地址0x0000%1=0符合要求。 第二个变量a的对齐值为4,默认对齐值也为4,所以其有效对齐值为4,也因为要存放在0x0004到0x0007这四个字节的地址空间上。第三个变量c,有效对齐值为2,所以存放在0x0008和0x0009这两个字节上。再看B,它自身的对齐值为其成员中最大对齐值,是b的4,所以结构体的有效对齐值也是4,为使结构圆整,B要是有效对齐值的整数倍,所以补0x0010和0x0011给结构B占用。(其实这是为结构数组考虑的)
具体请参考:http://www.360doc.com/content/10/0325/08/8442_20159163.shtml