【问题标题】:Struct padding rules in RustRust 中的结构填充规则
【发布时间】:2022-01-05 03:34:54
【问题描述】:

最近在学习 Rust 中的 Type Layout (https://doc.rust-lang.org/reference/type-layout.html) 时,我看到 Rust 中的 struct 支持 #[repr(C)] 指令,所以我想看看 default(Rust) 表示和类 C 表示。代码来了:

use type_layout::TypeLayout;

#[derive(TypeLayout)]
struct ACG1 {
    time1: u16, // 2
    time2: u16, // 2
    upper: u32, // 4
    lower: u16, // 2
}

#[derive(TypeLayout)]
#[repr(C)]
struct ACG2 {
    time1: u16, // 2
    time2: u16, // 2
    upper: u32, // 4
    lower: u16, // 2
}
fn main() {
    println!("ACG1: {}", ACG1::type_layout());
    println!("ACG2: {}", ACG2::type_layout());
}

我得到以下输出: 我了解填充 #[repr(C)] 结构的规则和整个结构的大小,但让我感到困惑的是 Rust 表示结构 ACG1,我找不到任何关于 Rust 填充规则的明确文档,并且我觉得padding的大小也应该包含在结构体的整体大小中,但是为什么ACG1的大小只有12字节呢?

顺便说一句,这是我用来协助打印结构布局的板条箱:https://crates.io/crates/type-layout

【问题讨论】:

  • 可能是箱子的问题?这有点令人困惑,因为 crate 的自述文件说 “此 crate 适用于非 #[repr(C)] 类型,但它们的布局是不可预测的。”,但是 crate 的作者在他的上面有 this open issue repo 声明,“今天的板条箱实际上不适用于非 repr(C) 类型。”
  • 在幕后,大小只是通过调用std::mem::size_of::<Type>() 来初始化,std::mem::size_of::<ACG1>() 确实返回 12。请参阅here 了解如何计算结构的大小。跨度>

标签: struct rust padding


【解决方案1】:

这个箱子似乎没有考虑字段重新排序。看来编译器将结构重新排序为首先具有upper

struct ACG1 {
    upper: u32,
    time1: u16,
    time2: u16,
    lower: u16,
}

这有点难看,但derive macro implementation 检查字段之间的区别按声明的顺序。所以从这个意义上说,结构的开头和第一个字段(time1)之间有 4 个字节的“填充”,第三个字段(upper)之间有 4 个字节的“填充”。 ) 和第四个字段 (lower)。有一个issue 归档它不适用于非#[repr(C)] 结构,因此我不建议为此目的使用此板条箱。

就 Rust 的规则而言,参考文献说 “[默认] 表示无法保证数据布局。” 所以理论上,编译器可以做任何它想做的事情并重新排序基于访问模式的字段。但在实践中,我认为按字段大小进行精心组织和组织并不是最小化填充的简单方法。

【讨论】:

  • 感谢您的回答并指出此板条箱的问题。我真正想知道的是:Rust 的编译器使用什么规则来布局结构,因为我以前的语言是 C++,所以我试图找出更基本的东西;但是当我查看Rust的文档时,似乎对结构布局最丰富的解释是结构的布局是不稳定且不可观察的,所以我有点难以接受
  • “Rust 的编译器使用什么规则来布局结构” - 没有规则!除了对齐之类的事情和字段等明显的事情不能重叠之外,编译器可以做它想做的事情。但目前,compiler/rustc_middle/src/ty/layout.rs 中的实现只是首先订购更高对齐的字段。但我相信未指定以允许将来进行更改或优化。 Rust 和 C++ 在语言的开发方式上有很大的不同。
  • 奇怪的是 crate 如何错误处理编译器重新排序,因为您应该能够使用 std::ptr::addr_of() 找到偏移量(考虑重新排序、填充和所有);但是,我想这是最近才引入的。
  • 我找到了一些有用的链接:1.camlorn.net/posts/April%202017/rust-struct-field-reordering 2.github.com/rust-lang/rust/pull/37429这些文章解释了rust为什么以及如何做到这一点,对这个问题感兴趣的人可以在这里找到有用的信息
【解决方案2】:

正如其他人所说,这似乎是板条箱中的问题。 最好问问编译器:

cargo clean
cargo rustc -- -Zprint-type-sizes

这会给你:

...
print-type-size type: `ACG1`: 12 bytes, alignment: 4 bytes
print-type-size     field `.upper`: 4 bytes
print-type-size     field `.time1`: 2 bytes
print-type-size     field `.time2`: 2 bytes
print-type-size     field `.lower`: 2 bytes
print-type-size     end padding: 2 bytes
print-type-size type: `ACG2`: 12 bytes, alignment: 4 bytes
print-type-size     field `.time1`: 2 bytes
print-type-size     field `.time2`: 2 bytes
print-type-size     field `.upper`: 4 bytes
print-type-size     field `.lower`: 2 bytes
print-type-size     end padding: 2 bytes

【讨论】:

    猜你喜欢
    • 2019-09-12
    • 2011-08-13
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 2015-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多