正在发生的是结构填充。
它由编译器完成,以确保元素位于对齐的内存地址。
您可以在此处阅读有关 x86/x86_64 中“对齐”的更多信息。
现在,为什么它们应该位于对齐的地址?(以 4 字节 WORD 为例):机器以“字”的形式从内存中访问数据。对于一个 4 字节的 WORD,这意味着要从地址读取单个字节b11001110
,您需要读取 4 个字节(地址中的最后 2 位基本上在读取时被忽略),然后选择您需要的字节一次WORD 在 CPU 中:
| b11001100 | b11001101 | b11001110 | b11001111 | <- all four loaded at once
\ /
only one used
当您开始读取更大的数据类型时,读取“未对齐”数据可能比读取对齐数据成本更高:
如果您想读取从 address 开始的 4 个字节(1 个字)b01110
,而不是一个字节,那么您必须读取 2 个字:
first load second load
/ \/ \
|01100|01101|01110|01111|10000|10001|10010|10011|
\ /
unaligned data read
编译器“填充”结构以避免此类读取。因为它们的成本很高。正如 Woodrow Douglass 在他们的回答中所建议的那样,您可以强制编译器“打包”而不是“填充”。
还有一件事:有些架构甚至不可能进行未对齐的负载。在此类机器上,操作系统通常会捕获在未对齐加载期间引发的异常,然后以某种方式模拟加载(例如,通过执行多个对齐加载)。