Zig 中的類型對齊
在 Zig 中,每種類型都有一個對齊方式,當從內存加載或向內存存儲該類型的值時,內存地址必須能被這個數平均整除。可以使用 @alignOf
查找任何類型的對齊方式。比如 @alignOf(u32)
會返回 4。
對齊取決於 CPU 架構,但始終是 2 的冪次,且小於 1 << 29
。
在 Zig 中,指針類型有一個對齊值。如果該值等於底層類型的對齊方式,則可以從類型中省略:
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
test "variable alignment" {
var x: i32 = 1234;
const align_of_i32 = @alignOf(@TypeOf(x));
try expect(@TypeOf(&x) == *i32);
try expect(*i32 == *align(align_of_i32) i32);
if (builtin.target.cpu.arch == .x86_64) {
try expect(@typeInfo(*i32).Pointer.alignment == 4);
}
}
從上面這句話可以反推出來,如果指針的對齊和底層類型不一致,則需要顯式定義,怎麼顯式定義呢?答案是:在定義變量或函數時指定,這樣指向他們的指針自然就是該對齊方式了。
test "set pointer alignment" {
const foo: u32 = 0x11223344;
const p = &foo;
try std.testing.expectEqual(@TypeOf(p), *align(4) const u32);
const foo2: u32 align(64) = 0x11223344;
const p2 = &foo2;
try std.testing.expectEqual(@TypeOf(p2), *align(64) const u32);
}
稍微利用一點數學知識我們可以知道,大的對齊方式,可以轉成小的,但反之則不一定。 @alignCast
就是幹這事的,它會在編譯期進行安全檢查 [1],不會有運行時開銷。
test "testBadAlignCast" {
const foo: u32 = 0x11223344;
const p = &foo;
try std.testing.expectEqual(@TypeOf(p), *align(4) const u32);
const p2: *align(1 << 15) const u32 = @alignCast(p);
try std.testing.expectEqual(@TypeOf(p2), *align(1 << 15) const u32);
std.debug.print("{x}\n", .{p2.*});
std.debug.print("{*}\n", .{p2});
}
上面這個例子強制把對齊爲 4 的 p 轉成對齊是 32768 的 p2,測試竟然通過了,通過 p2.*
打印,值也是對的
11223344
p2 addr: u32@10051a500
但其地址明明不是 32768 的整數倍!(0x10051a500 / (1<<15) = 131235.2890625),這應該是 Zig 的 bug?
Zig 官網給了另一種對齊失敗的例子,這個是能檢測出來的:
test "alignCastPanic" {
const array = [_]u32{ 0x11223344, 0x55667788 };
const bytes = mem.sliceAsBytes(array[0..]);
const slice4 = bytes[1..5];
const int_slice = mem.bytesAsSlice(u32, @as([]align(4) const u8, @alignCast(slice4)));
try std.testing.expectEqual(@TypeOf(int_slice), *align(4) const u32);
}
報錯信息如下:
3/3 align-demo.test.alignCastFail... thread 7925321 panic: incorrect alignment
/Users/jiacai/.asdf/installs/zig/master/lib/compiler/align-demo.zig:37:70: 0x102dd92fb in test.alignCastFail (test)
/Users/jiacai/.asdf/installs/zig/master/lib/compiler/test_runner.zig:158:25: 0x102de2b0b in mainTerminal (test)
if (test_fn.func()) |_| {
可見 Zig 只有在這種十分明確的情況下(slice4 相當於向前移動了一位,地址肯定是奇數),才能檢查出來對齊失敗。
- • 更多參考: proposal: reform how align works[2]
ZigCC
-
• 日報地址:https://github.com/zigcc/forum/issues/110
-
• 論壇地址:https://ask.ziglang.cc/
引用鏈接
[1]
安全檢查: https://ziglang.org/documentation/master/#Incorrect-Pointer-Alignment
[2]
proposal: reform how align works: https://github.com/ziglang/zig/issues/7465
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/e5GDjWxMuLgSmQJvXUA27g