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(<< 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 相當於向前移動了一位,地址肯定是奇數),才能檢查出來對齊失敗。

ZigCC

引用鏈接

[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