Rust中的结构体填充规则

6

最近我在学习Rust中的类型布局时,发现Rust中的结构体支持#[repr(C)]指令,因此我想看看默认(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());
}

我得到以下输出:

Enter image description here

我理解如何填充#[repr(C)]结构体以及整个结构体的大小的规则,但让我困惑的是Rust表示结构体ACG1。我找不到有关Rust填充规则的清晰文档,我认为填充大小也应包括在结构体的总大小中,但为什么ACG1的大小只有12个字节?
顺便说一下,这是我用来协助打印结构布局的crate:type-layout 0.2.0

1
可能是与 crate 有关的问题?这有点令人困惑,因为 crate 的 readme 上说:“此 crate 可用于非 #[repr(C)] 类型,但它们的布局是不可预测的。”,但 crate 的作者在他的 repo 上有一个未解决的问题(https://github.com/LPGhatguy/type-layout/issues/4),其中指出:“今天的 crate 实际上不能用于非 repr(C) 类型。” - Herohtar
1
在幕后,大小只是通过调用std::mem::size_of::<Type>()来初始化的,而std::mem::size_of::<ACG1>()确实返回12。请参见此处以了解如何计算结构体的大小。 - Herohtar
2个回答

5

这个 crate 似乎没有考虑到字段重排。看起来编译器对结构体进行了重新排序,将 upper 放在了首位:

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

有点难以看清,但是派生宏实现会检查字段按照声明的顺序之间的差异。因此在这个意义上,结构体开头和第一个字段(time1)之间有四个字节的"填充",第三个字段(upper)和第四个字段(lower)之间有四个字节的"填充"。

一份问题记录表明,它不适用于非#[repr(C)]结构体,因此我不建议为此目的使用该包。

就Rust规则而言,参考文献说:"[默认]表示并没有对数据布局做出任何保证。" 因此理论上,编译器可以根据访问模式对字段进行重排序。但实际上,我认为它并不那么复杂,按字段大小排序是最简单的最小化填充的方法。


谢谢您的回答并指出这个 crate 的问题。我实际上想知道的是:Rust 编译器使用什么规则来布置结构,因为我的以前的语言是 C++,所以我正在尝试找出更基本的东西;但是当我查看 Rust 的文档时,似乎对于结构的布局最丰富的解释是结构的布局不稳定和不可观测,所以我有点难以接受。 - hxb1996
1
“Rust编译器用什么规则来布置结构体?”- 没有固定的规则!除了对齐和字段不能重叠等明显的事情,编译器可以随意操作。目前在compiler/rustc_middle/src/ty/layout.rs中实现的规则只是将更高对齐度的字段放在前面。但我认为这是未指定的,以便将来进行更改或优化。 Rust和C ++在语言开发方面有很大的不同。 - kmdreko
奇怪的是,这个箱子处理编译器重新排序的方式有问题,因为你应该能够使用 std::ptr::addr_of() 找到偏移量(考虑重新排序、填充等),但我猜这可能是最近才引入的。 - Coder-256
我找到了一些有用的链接:
  1. https://camlorn.net/posts/April%202017/rust-struct-field-reordering
  2. https://github.com/rust-lang/rust/pull/37429 这些文章解释了为什么和如何rust这样做,对于对此问题感兴趣的人可能会在这里找到有用的信息。
- hxb1996

4

就像其他人所说的,这似乎是在创建时出现了问题。最好向编译器询问:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接