一个 Zig 编译器的 Bug
在动手学习 Zig 的过程中,在探索stack中变量的memory layout时,发现了了一个 Bug,已提交到 github。 在这里记录一下:
const std = @import("std");
const SIZE = 1024 * 256;
pub fn main() !void {
var arr: [SIZE]u32 = undefined;
for (arr, 0..) |_, i| {
arr[i] = @intCast(i);
}
std.debug.print("main &arr = {*}\n", .{&arr});
std.debug.print("main &arr = {*} same as above \n", .{&arr}); // same as above
std.debug.print("\npassArray for mutable array\n", .{});
passArray(arr);
}
fn passArray(arr: [SIZE]u32) void {
const p1: *const [SIZE]u32 = &arr;
const p2 = &arr;
const p3: [*]const u32 = &arr;
std.debug.print("inside passArray &arr = {*} p2 = {*} p3 = {*} p1 != p2 != p3 \n", .{ p1, p2, p3 });
const LOOP = 3; // when LOOP = 14, the program will crash
std.debug.print("LOOP = {} \n", .{LOOP});
inline for (0..LOOP) |_| {
std.debug.print("inside passArray, &arr = {*} not same as above.\n", .{&arr}); // &arr increase SIZE * 4 every time
}
}
因为每次 &arr
操作都导致在栈上复制了一个数组,因此,如果数组长度较大,&arr
操作次数较多时,例如,在上述的代码中,1M * 14 = 14M, 在我
的 Mac 上,就会出现 SIGSEV 错误 ( 应该是 StackOverflow 了 )。
在国内的 Zig 群问了一下,众说纷纭,有大神坚持认为这个不是 bug,而是 constcast 的必然结果,不过我并不能理解:
&arr
只是一个取地址操作,并不会改变数据类型。如果原来是 const 的,结果就是*const [N]
否则就是*[N]
- 不同于 rust, zig 并没有
&x
和&mut x
的区别。 &arr
导致数组复制,不仅会导致栈内存的浪费,而且也增加了不必要的代码成本。更严重的会导致 StackOverflow,其实还是一个比较严重的问题。
提交到 github 上,很快获得了 core team 的确认,已接受作为一个Bug,并添加到了 0.14 的 milestone 中。