微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么元组或结构的大小不是成员的总和? 大小和对齐方式

如何解决为什么元组或结构的大小不是成员的总和? 大小和对齐方式

assert_eq!(12,mem::size_of::<(i32,f64)>()); // Failed
assert_eq!(16,f64)>()); // succeed
assert_eq!(16,f64,i32)>()); // succeed

为什么不是 12 (4 + 8)? Rust 对元组有特殊处理吗?

解决方法

为什么不是 12 (4 + 8)? Rust 对元组有特殊处理吗?

没有。常规结构可以(并且确实)具有相同的“问题”。

答案是padding:在 64 位系统上,f64 应该对齐到 8 个字节(即它的起始地址应该是 8 的倍数)。结构通常具有其最受约束(最大对齐)成员的对齐方式,因此元组的对齐方式为 8。

这意味着您的元组必须从 8 的倍数的地址开始,因此 i32 从 8 的倍数开始,以 4 的倍数结束(因为它是 4 个字节),并且编译器添加了 4 个字节的填充,以便 f64 正确对齐:

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
[ i32 ] padding [     f64     ]

“但是等等”,你喊道,“如果我反转元组的字段,大小不会改变!”。

确实如此:上面的架构并不准确,因为默认情况下 rustc 会将您的字段重新排序为紧凑的结构,因此它真的会这样做:

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
[     f64     ] [ i32 ] padding 

这就是为什么你的第三次尝试是 16 字节:

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
[     f64     ] [ i32 ] [ i32 ]

而不是 24:

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
[  32 ] padding [     f64     ] [  32 ] padding 

“抱紧你的马”,你说,眼睛敏锐,“我可以看到 f64 的对齐方式,但为什么最后有填充?那里没有 f64!”

好吧,这样计算机就可以更轻松地处理序列:具有给定对齐方式的结构也应该具有其对齐方式的倍数,这样当您有多个序列时:

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
[     f64     ] [ i32 ] padding [     f64     ] [ i32 ] padding 

它们正确对齐并且如何放置下一个的计算很简单(只是被结构的大小偏移),它还避免将这些信息到处 em>。基本上,数组 / vec 本身永远不会被填充,而是填充它所包含的内容。

请注意,您可以告诉 Rust 完全按照您使用 repr(C) 给出的顺序放置您的结构(这不是元组的选项)。这是安全的,并且通常没有用,但在某些边缘情况下它很重要。我认为最常见的原因是与外部代码(需要非常具体的布局)交互或避免在高性能代码中使用 false sharing

您还可以使用 repr(packed) 告诉 rustc 不要填充结构。这风险更大,因为它通常会降低性能(大多数 CPU 与未对齐的数据交叉),并且可能会导致程序崩溃或在某些架构上完全返回错误的数据。

,

虽然@Masklinn 已经provided a general answer 认为这是由于对齐,这里是 Rust 参考:

大小和对齐方式

所有值都有一个对齐方式和大小。

值的对齐指定了哪些地址可用于存储值。 对齐值 n 只能存储在 n 倍数的地址。例如,对齐为 2 的值必须存储在偶数地址,而对齐为 1 的值可以存储在任何地址。对齐以字节为单位,并且必须至少为 1,并且始终为 2 的幂。可以使用 align_of_val 函数检查值的对齐情况。

值的大小是具有该项目类型(包括对齐填充)的数组中连续元素之间的偏移量(以字节为单位)。值的大小始终是其对齐方式的倍数。可以使用 size_of_val 函数检查值的大小。

[...]

The Rust Reference - Type Layout - Size and Alignment

(强调我的)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。