charlie
1
While experimenting with a dynamic buffer, I noticed a weird thing: one could use labeled blocks to resolve invalid pointers.
somehow this works:
const char: [:0]u8 = switch (handler.mode) {
.binary, .decimal => Buf: {
var buf: [2]u8 = undefined;
break :Buf try std.fmt.bufPrintZ(&buf, "{d}", .{rand_int});
},
.hexadecimal => hexBuf: {
var buf: [3]u8 = undefined;
break :hexBuf try std.fmt.bufPrintZ(&buf, "{c}", .{assets.hex_chars[rand_int]});
},
.textual => texBuf: {
var buf: [4]u8 = undefined;
break :texBuf try std.fmt.bufPrintZ(&buf, "{u}", .{assets.tex_chars[rand_int]});
},
};
Is this a bug or undocumented magic?
dimdin
2
It is neither a zig bug nor magic.
Zig does not guarantee anything about dangling pointers. And what you found is not something that can only happen in labeled blocks. For example:
fn foo() ![:0]u8 {
var buf: [2]u8 = undefined;
return try std.fmt.bufPrintZ(&buf, "{d}", .{rand_int});
}
This is code that perfectly compiles, but foo()
returns a dangling pointer in stack.
1 Like
charlie
3
I could see the compiler allows this, but I did not expect it to work—usually I am getting far from valid output in such cases.
dimdin
4
It works because the compiler allocates stack on function entry and not on each block. But it is undefined behavior.
4 Likes
I think that would be a great addition to the pointers to temporary memory doc.
2 Likes