问题一:为什么32位CPU只能读取字长(4字节)倍数的地址
答:
因为32位计算机的存储系统通常使用4组存储体,存储一个字长(4字节)数据时采用分体存储方式,即4个存储体各存储1个字节,且低二位(A0和
A1)地址线未用作选址,因而CPU在寻址时只能寻到以字长(4字节)为倍数的地址。同理,16位计算机系统普遍采用两组存储体(奇/偶)分体存储数据,
且低一位(A0)地址线未用作选址,从而导致基于这种设计的CPU只能寻到以2为倍数的地址。
问题二:什么是“自然对齐”?
答:如果一个数据的地址是它类型长度的整数被,那么这个数据就是“自然对齐”的。
问题三:为什么如果多字节型数据不对齐会浪费额外的CPU时钟?
答:
以32位系统为例,假设一个int型数据的地址是0x0004,由于它是对齐的,即CPU可以寻到0x0004这个地址,所以CPU可以一次性将
0x0004 -
0x0007这4个字节的数据取出或存入,但如果它的地址是0x0001,由于CPU在它附近只能寻到0x0000和0x0004这两个地址,所以需要分
别从这两个地址取出数据后拼接得到目标数据。
问题四:为什么定义结构体时,要在末尾用空字节将该结构体长度填充至“长度最大的成员”的长度的整数倍?
答:在接连定义多个结构体对象(或定义结构体数组)时,如果结构体没有自然对齐的话,会导致后续很多结构体内多字节型成员的地址不能满足“自然对齐”,下面举两个例子对比说明。
例一:
struct
{
long k; //4字节
char a[5]; //1字节 * 5 = 5字节
char pad[3];//1字节 * 3 = 3字节
} //该结构体长度为12字节,满足最长成员类型(long)长度(4字节)的整数倍
假设定义一个拥有2个元素的结构体数组,且首成员long的偏移为0,那么这两个元素内成员的偏移为:
元素1_long: 0
元素1_a: 4
元素1_pad: 9
元素2_long: 12
元素2_a: 16
元素2_pad: 21
*元素3_long: 24
由此可见,结构体自然对齐时,定义多个结构体时各自成员必将自然对齐,下面再看一个例子。
例二:
struct
{
long k; //4字节
char a[5]; //1字节 * 5 = 5字节
} //该结构体长度为9字节,不满足最长成员类型(long)长度(4字节)的整数倍
同样假设定义一个拥有2个元素的结构体数组,且首成员long的偏移为0,那么这两个元素内成员的偏移为:
元素1_long: 0
元素1_a: 4
元素2_long: 9 //未自然对齐
元素2_a: 13
在这个例子里,结构体的长度没有被填充至long型长度的整数倍,这导致了结构体数组中第2个元素中的long型成员不满足自然对齐条件(即第2个数组元素没有自然对齐),这里元素2_long的内容存放在
0x0009 -
0x000c这4个字节的空间内,但CPU在它附近只能寻址0x0008、0x000c这两个地址,其对应的字长空间分别为0x0008 -
0x000b和0x000c - 0x000f,这就需要分别从0x0008和0x000c两个地址取数据后拼接得到0x0009处的long型数据。
这就是对结构体型之所以那样补足长度的原因了。
问题四:如果结构体含double型成员,是对该结构体长度填充至字长倍数还是双字长倍数?
答:我个人认为对x86系列CPU来说,填充至字长倍数即可(32位的话对应是4字节对齐),且gcc在这种情况下是这样默认的。至于网上说按8字节对齐的资料,观察后发现它们都交代了诸如“比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始”这样的前提。所以个人推测“将长度填充至长度最大的成员的长度的倍数”这条规则是充分考虑跨平台移植性而定下的,在x86系列平台最长按字长的倍数对结构体类型进行填充即可。
后记:
不得不承认,对于第四个问题我还是心存疑惑,真心希望能得到大家的指教。
阅读(1308) | 评论(0) | 转发(0) |