我有一个大小为48字节的结构,最近在里面添加了另一个大小为20字节的结构;所以结构的总大小变成了68字节。(所有成员均未签名)
typedef struct{
...
...
unsigned long long a1;
struct s2
}s1 ;
S1 结构是一个数组;如果使用 s1++ 递增 s1,则观察到 s1 递增 72 字节而不是 68 字节。令人惊讶的是,如果从 s2 中删除结构 s1,一切正常,即 s1 增加 48 个字节。任何关于原因的指示都会有所帮助。
您没有说您正在使用哪个编译器或平台,但很可能编译器正在添加对齐字节以使结构在 8 字节边界上对齐。 这是很常见的。
正在发生的事情是结构填充。
它由编译器完成,以确保元素驻留在对齐的内存地址。
您可以在此处阅读有关 x86/x86_64 中"对齐"的更多信息。
现在,为什么它们应该在对齐的地址?(以 4 字节 WORD 为例):机器以"单词"的形式访问内存中的数据。对于 4 字节的 WORD,这意味着要从地址b11001110
读取单个字节,您需要读取 4 个字节(地址中的最后 2 位基本上在读取时被忽略),然后在 WORD 在 CPU 中后选择您需要的字节:
| b11001100 | b11001101 | b11001110 | b11001111 | <- all four loaded at once
/
only one used
当您开始读取更大的数据类型时,读取"未对齐"基准的成本可能高于读取对齐的数据:
如果要从地址b01110
开始读取 4 个字节(1 个单词),而不仅仅是一个字节,那么您必须读取 2 个单词:
first load second load
/ /
|01100|01101|01110|01111|10000|10001|10010|10011|
/
unaligned data read
编译器"pad"结构以避免此类读取。因为它们很昂贵。正如伍德罗·道格拉斯(Woodrow Douglass)在他们的答案中所建议的那样,你可以强制编译器使用"pack"而不是"pad"。
还有一件事:有些架构甚至不可能实现未对齐的负载。在此类机器上,操作系统通常会捕获未对齐负载期间引发的异常,然后以某种方式模拟负载(例如,通过执行多个对齐加载)。
这是一台 64 位机器吗? 72 字节是 64 位字边界。编译器正在对齐您的结构。在 gcc 中,如果你用 __attribute__((__packed__))
声明你的结构,你可以避免这种情况。
所以像这样声明你的 typedef:
typedef struct {
...
...
struct s2;
} __attribute__((__packed__)) s1;