如何解决为什么元组或结构的大小不是成员的总和? 大小和对齐方式
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 举报,一经查实,本站将立刻删除。