对齐和大小端 (Alignment and Endian)

各种架构支持类似的原始数据类型,如,字节/byte,half word,word,double word,等等。

不同的指令被设计用来处理指定的数据类型。举个例子,加载一个字节的指令跟加载一个word的指令不一样。

一些架构,比如SPARC,需要一个内存索引的地址恰当地对齐。比如,一个word(C/C++中的整数)必须四字节对齐,这意味着对应的地址必须可以被4整除。

如果地址没有按要求对齐,那么一个硬件异常就会丢出。这常常会在应用程序转换为一个Bus错误信号。

其他一些架构,比如x86家族,没有这样的严格要求。但是如果数据不对齐,它可能会在特定的场合下带来性能损耗。

正因为如此,所有的编译器默认会把数据放在合适的对齐地方,即使在那些不强制要求对齐的架构。

C/C++数据类型,如字符,短整数,整数,长整数,浮点数,双浮点数,等等,在目标架构里有对应的数据类型。

所以,编译器会相应地对齐这些数据类型。对于整合的数据类型,比如结构体和数组,编译器必须确保所有的数据字段在任何的嵌套层面对齐。

结构体的对齐要求是所有单个字段的最大要求。

数组的对齐要求是和数组里面每个元素的一样。

如果整合类型有多个层级,这些规则适用于所有的层级。举个例子,下面这个C结构体,

struct aggr_type{
    char c;
    int i; 
    short s;
    double d;};

在所有的字段中,字段d的对齐要求最大,8个字节。所以,这个结构体aggr_type需要按8字节对齐。

它同时也需要一些填充,从而确保每个字段满足它的对齐要求。

图4-1描绘了上面的结构体对应的填充(灰色的方框)。字段c一共有3个字节的填充,字段s有6个字节的填充。这些填充使得紧接的字段i和字段d相应地对齐在需要的4字节和8字节。

图片

(XT:有一些面试题,喜欢问怎么排列相应的字段,从而更省空间。)

当编译器分配栈上的变量时,它会确保每一个变量,原始类型或者是整合类型,满足它的对齐要求。

除此以外,ABI也会指出整个栈帧需要对齐的某个最小值,从而保证每一个栈上的局部变量和系统的数据可以合适地对齐。所以本书出现的栈变量之间有很多对齐填充并非不常见。

堆里分配的数据对象也要满足同样的要求。内存管理器只知道请求的内存块大小。它根本不知道背后的数据对象的数据类型,也不知道它的对齐要求。

为了正确地工作,内存管理器确保返回的内存块对齐在目标架构尽可能大的要求。尽管这意味着一些空间的浪费,因为实际的数据对象的对齐可能不需要这么多。

(XT,举个例子可能会更好理解,如果申请32字节,那么返回的内存块的地址必须是可以整除32,如果是1024个字节,那么是不是必须整除1024呢?)