更新

2024-07-05 本书已经出版 image 由于本库的草稿是我之前一个人写的,所以质量和正确性都不如经过两位作者和出版社编辑审阅和校正过的书稿。 如果你想阅读更加完善的版本,推荐购买正版书籍。

对齐和大小端 (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呢?)