差异是由于padding 为了满足alignment 类型的要求。特定类型的值不希望存在于任意地址,而只希望存在于可被类型的对齐整除的地址。例如,以char 为例:它的对齐方式为4,因此它只想生活在可被4 整除的地址,如0x4、0x8 或0x7ffd463761bc,而不是0x6 或0x7ffd463761bd.
类型的对齐方式取决于平台,但通常大小为1、2 或4 的类型也分别具有1、2 和4 的对齐方式。 1 的对齐意味着该类型的值在任何地址都感觉舒适(因为任何地址都可以被 1 整除)。
那么现在你的结构呢?在 Rust 中,
复合结构的对齐方式等于其字段对齐方式的最大值。
这意味着您的MyStruct 类型的对齐方式也是4。我们可以通过mem::align_of() 和mem::align_of_val() 进行检查:
// prints "4"
println!("{}", mem::align_of::<MyStruct>());
现在假设你的结构体的值位于0x4(满足结构体的直接对齐要求):
0x4: [obj.foo]
0x5: [obj.bar's first byte]
0x6: [obj.bar's second byte]
0x7: [obj.bar's third byte]
0x8: [obj.bar's fourth byte]
糟糕,obj.bar 现在住在0x5,虽然它的对齐是 4!那很糟!
为了解决这个问题,Rust 编译器将所谓的 padding(未使用的字节)插入到结构中。在内存中它现在看起来像这样:
0x4: [obj.foo]
0x5: padding (unused)
0x6: padding (unused)
0x7: padding (unused)
0x8: [obj.bar's first byte]
0x9: [obj.bar's second byte]
0xA: [obj.bar's third byte]
0xB: [obj.bar's fourth byte]
为此,MyStruct 的大小为 8,因为编译器添加了 3 个填充字节。现在一切又好了!
...除了浪费的空间?的确,这是不幸的。一个解决方案是交换结构的字段。幸运的是,与 C 或 C++ 不同,Rust 中结构的内存布局是未指定的。特别是,Rust 编译器允许更改字段的顺序。您不能假设obj.foo 的地址低于obj.bar!
从 Rust 1.18 开始,这种优化由编译器执行。
但即使使用更新或等于 1.18 的 Rust 编译器,您的结构仍然是 8 字节大小。为什么?
内存布局还有一条规则:结构的大小必须始终是其对齐方式的倍数。这对于能够在数组中密集布局这些结构很有用。假设编译器将重新排序我们的结构字段并且内存布局如下所示:
0x4: [obj.bar's first byte]
0x5: [obj.bar's second byte]
0x6: [obj.bar's third byte]
0x7: [obj.bar's fourth byte]
0x8: [obj.foo]
看起来像 5 个字节,对吧?没有!想象一下有一个数组[MyStruct]。在数组中,所有元素在内存中彼此相邻:
0x4: [[0].bar's first byte]
0x5: [[0].bar's second byte]
0x6: [[0].bar's third byte]
0x7: [[0].bar's fourth byte]
0x8: [[0].foo]
0x9: [[1].bar's first byte]
0xA: [[1].bar's second byte]
0xB: [[1].bar's third byte]
0xC: [[1].bar's fourth byte]
0xD: [[1].foo]
0xE: ...
糟糕,现在数组的第二个元素bar 开始于0x9!所以实际上,数组大小需要是其对齐的倍数。因此,我们的记忆是这样的:
0x4: [[0].bar's first byte]
0x5: [[0].bar's second byte]
0x6: [[0].bar's third byte]
0x7: [[0].bar's fourth byte]
0x8: [[0].foo]
0x9: [[0]'s padding byte]
0xA: [[0]'s padding byte]
0xB: [[0]'s padding byte]
0xC: [[1].bar's first byte]
0xD: [[1].bar's second byte]
0xE: [[1].bar's third byte]
0xF: [[1].bar's fourth byte]
0x10: [[1].foo]
0x11: [[1]'s padding byte]
0x12: [[1]'s padding byte]
0x13: [[1]'s padding byte]
0x14: ...
相关: