Luk*_*odt 3 struct memory-alignment memory-layout rust
我试图测量结构及其字段(Playground)的大小:
use std::mem;
struct MyStruct {
foo: u8,
bar: char,
}
println!("MyStruct: {}", mem::size_of::<MyStruct>());
let obj = MyStruct { foo: 0, bar: '0' };
println!("obj: {}", mem::size_of_val(&obj));
println!("obj.foo: {}", mem::size_of_val(&obj.foo));
println!("obj.bar: {}", mem::size_of_val(&obj.bar));
Run Code Online (Sandbox Code Playgroud)
该程序打印:
MyStruct: 8
obj: 8
obj.foo: 1
obj.bar: 4
Run Code Online (Sandbox Code Playgroud)
因此结构的大小大于其字段大小的总和(这将是5).这是为什么?
差异是由于填充以满足类型对齐要求.特定类型的值不希望存在于任意地址,而是仅存在于可被类型对齐整除的地址处.例如,取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>());
Run Code Online (Sandbox Code Playgroud)
现在假设你的struct的值存在0x4(满足struct的直接对齐要求):
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]
Run Code Online (Sandbox Code Playgroud)
哎呀,obj.bar现在住0x5,虽然它的排列是4!那很糟!
为了解决这个问题,Rust编译器将所谓的padding(未使用的字节)插入到struct中.在内存中它现在看起来像这样:
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]
Run Code Online (Sandbox Code Playgroud)
出于这个原因,大小MyStruct为8,因为编译器添加了3个填充字节.现在一切都很好!
......除了浪费的空间?实际上,这是不幸的.解决方案是交换struct的字段.幸运的是,为了这个目的,Rust中的结构的内存布局是未指定的,与C或C++不同.特别是,允许Rust编译器更改字段的顺序.你不能认为obj.foo地址低于obj.bar!
从Rust 1.18开始,这个优化由编译器执行.
但即使Rust编译器更新或等于1.18,您的结构仍然是8字节大小.为什么?
内存布局还有另一个规则:结构的大小必须始终是其对齐的倍数.这对于能够在阵列中密集布局这些结构非常有用.假设编译器将重新排序我们的struct字段,内存布局如下所示:
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]
Run Code Online (Sandbox Code Playgroud)
看起来像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: ...
Run Code Online (Sandbox Code Playgroud)
哎呀,现在阵列的第二个元素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: ...
Run Code Online (Sandbox Code Playgroud)
相关: