简短的回答是,处理器中的基本对象的大小是 2 的小幂(例如,1、2、4、8 和 16 字节),并且内存按大小是 2 的小幂(例如,8字节),因此必须对齐结构才能很好地适应这些大小。
长答案是,其原因是基于物理学和初等数学。计算机自然而然地使用值为 0 和 1 的位。这是因为很容易设计在两个值之间切换的物理事物:高电压和低电压,是否存在电荷,等等。区分三个值更难,因为您必须对值之间的转换更加敏感。因此,随着计算机技术几十年来的发展,我们使用了比特(二进制数字)而不是像三进制数字这样的替代品。
为了产生更大的数字,我们组合了多个位。所以两位可以结合起来有四个值。三位可以有八个值,依此类推。在较旧的计算机中,有时位一次被分组为 6 个或 10 个。然而,八个变得普遍,并且现在基本上是标准的。使用 8 位作为一个字节并不像我描述的其他一些分组那样有很强的物理原因,但它是世界的方式。
计算机的另一个特点是内存。一旦我们有了这些字节,我们就想将它们中的很多存储在一个处理器可以轻松访问的设备中,这样我们就可以快速地将大量字节输入和输出处理器。当我们有很多字节时,我们需要一种方法让处理器告诉内存处理器想要读取或写入哪些字节。所以处理器需要一种方法来寻址字节。
处理器使用位来表示值,因此它将使用位来表示地址值。因此,内存将被构建为接受位,以指示在处理器读取时向处理器提供哪些字节,或者在处理器写入时存储哪些字节。存储设备如何处理这些位?一件简单的事情是使用一个位来控制通往记忆的路径的一个开关。内存将由许多存储字节的小部分组成。
考虑内存设备中可以存储一个字节的东西,并考虑其中两个彼此相邻的东西,比如 A 和 B。我们可以使用开关来选择是希望 A 字节处于活动状态还是 B 字节处于活动状态积极点。现在考虑其中的四个,比如 A、B、C 和 D。我们可以使用一个开关来选择是使用 AB 组还是使用 CD 组。然后另一个开关选择 A 或 B(如果使用 AB 组)或 C 或 D(如果使用 CD)组。
这个过程继续:内存地址中的每个位选择一组要使用的存储单元。1位选择2个存储单元,2位选择4个,3位选择8个,4位选择16个,以此类推。8位选择256个存储单元,24位选择16,777,216个存储单元,32位选择4,294,967,296个存储单元。
还有一种复杂情况。在处理器和内存之间移动单个字节很慢。相反,现代计算机将内存组织成更大的部分,例如 8 个字节。您一次只能在内存和处理器之间移动八个字节。当处理器请求内存提供一些数据时,处理器只发送地址的高位——低三位选择八个字节内的单个字节,它们不发送到内存。
这更快,因为处理器在其他情况下让内存完成所有切换以提供一个字节所需的时间获得八个字节,并且它更便宜,因为您不需要大量额外的开关来区分个体内存中的字节。
但是,现在这意味着处理器无法从内存中获取单个字节。当您执行访问单个字节的指令时,处理器必须从内存中读取八个字节,然后在处理器内部移动这些字节以获得您想要的一个字节。同样,为了获得两个或四个字节,处理器读取八个字节并仅提取您想要的字节。
为了简化这个过程,处理器设计人员指定数据应该以某种方式对齐。通常,它们需要将 2 字节数据(如 16 位整数)与 2 字节的倍数对齐,将 4 字节数据(如 32 位整数和 32 位浮点值)与 4 的倍数对齐字节和八字节数据对齐为八字节的倍数。
这种所需的对齐有两个效果。首先,由于四字节数据只能从内存读取的八字节块中的两个位置开始(开头或中间),处理器设计人员只需插入电线即可从两个位置提取四个字节。他们不需要添加所有额外的线来从八个单独的字节中的任何一个中提取四个字节,如果允许任何对齐,这些字节可能是起始位置。(一些处理器将完全禁止加载未对齐的数据,而一些处理器将允许它,但使用缓慢的方法来提取它,使用更少的线路但使用迭代算法在多个处理器周期内移动数据,因此未对齐的加载很慢。)
第二个影响是,因为四字节数据只能从八字节块中的两个位置开始,它也在该块内结束。考虑一下如果您尝试加载从 8 字节块的第 6 个字节开始的 4 字节数据会发生什么。前两个字节在块中,但接下来的两个字节在内存中的下一个块中。处理器必须从内存中读取两个块,从每个块中获取不同的字节,然后将这些字节放在一起。这比只读取一个块要慢得多。
因此,内存是按 2 的幂来组织的,因为这是位的自然结果,而处理器需要对齐,因为这样可以提高内存访问的效率。对齐自然是 2 的幂,这就是为什么当结构大小是用于对齐的 2 的幂的倍数时,结构尺寸会更好地工作。