为什么将一个枚举变量设为f64会增加该枚举的大小?

Riv*_*Tam 3 enums rust

我创建了三个几乎相同的枚举:

#[derive(Clone, Debug)]
pub enum Smoller {
    Int(u8),
    Four([u8; 4]),
    Eight([u8; 8]),
    Twelve([u8; 12]),
    Sixteen([u8; 16]),
}

#[derive(Clone, Debug)]
pub enum Smol {
    Float(f32),
    Four([u8; 4]),
    Eight([u8; 8]),
    Twelve([u8; 12]),
    Sixteen([u8; 16]),
}

#[derive(Clone, Debug)]
pub enum Big {
    Float(f64),
    Four([u8; 4]),
    Eight([u8; 8]),
    Twelve([u8; 12]),
    Sixteen([u8; 16]),
}

pub fn main() {
    println!("Smoller: {}", std::mem::size_of::<Smoller>()); // => Smoller: 17
    println!("Smol: {}", std::mem::size_of::<Smol>()); // => Smol: 20
    println!("Big: {}", std::mem::size_of::<Big>()); // => Big: 24
}
Run Code Online (Sandbox Code Playgroud)

鉴于对计算机和内存的了解,我期望它们的大小应相同。最大的变体是[u8; 16]大小为16的。因此,尽管这些枚举的第一个变体的大小确实有所不同,但它们具有的最大变体的大小相同,并且变体总数相同。

我知道Rust可以做一些优化来确认某些类型何时存在间隙(例如,指针会折叠,因为我们知道它们将无效且为0),但这确实是相反的。我认为,如果我是手工构造此枚举,我可以将其装入17个字节(辨别只需要一个字节),因此20个字节和24个字节都让我感到困惑。

我怀疑这可能与对齐有关,但是我不知道为什么,也不知道为什么有必要。

有人可以解释吗?

谢谢!

mca*_*ton 8

大小必须至少为17个字节,因为它的最大变体为16个字节,并且需要一个额外的字节来进行判别(在某些情况下,编译器可以很聪明,将判别式放入变体的未使用位中,但是无法在这里执行此操作)。

同样,的大小Big必须为8字节的倍数才能正确对齐 f64。大于17的8的较小倍数是24。类似地,Smol不能仅是17个字节,因为它的大小必须是4个字节(的大小f32)的倍数。Smoller仅包含,u8因此可以对齐到1个字节。

  • 我明白了,所以它们之间的差异可以通过对齐来解释——“f64”需要 8 字节对齐,“f32”需要 4 字节对齐,而“u8”不需要任何对齐。我想我的下一个问题必须是......为什么整个事情必须保持一致?难道您不能将第一个(或第二个;我们有 17 个)8 个字节视为对齐的浮点而不对齐整个枚举吗? (2认同)
  • 明白了-正如@rodrigo指出的(以及链接到的页面也涵盖了),整个struct / enum必须对齐,因为如果不对齐,则值数组的内部成员将不对齐。即使`Big`以浮点数开头,如果整个枚举为17个字节,则[[Big]`中的下一个`Big`也将从字节18开始,因此,即使它的内部对齐方式,*对齐*也将是错误的对齐很好。感谢您和@rodrigo的帮助! (2认同)