Memory Question

Why did variables within a stack function revert to 0 in Zig version 0.14.0 if they were called again? This was logical, as the function’s scope had expired. However, in Zig version 0.15.0, do the values ​​persist?

fn printMem(param: *const u8, constant: *const u8, variable: *const u8) void {
    std.debug.print("param: {x}\nconst: {x}\nvar: {x}\n\n", .{
        @intFromPtr(param),
        @intFromPtr(constant),
        @intFromPtr(variable),
    });
}

fn c(p: u8) struct { *const u8, *u8 } {
    const lc: u8 = 3;
    var lv: u8 = 99;
    std.debug.print("c: \n", .{});
    printMem(&p, &lc, &lv)

    return .{ &lc, &lv };
}

pub fn main() !void {
    const ptr_to_const, const ptr_to_var = c(15);
    // Pointer to const and local var.
    std.debug.print("\nptr_to_const: {x} = {}\nptr_to_var: {x} = {}\n\n", .{
        @intFromPtr(ptr_to_const),
        ptr_to_const.*,
        @intFromPtr(ptr_to_var),
        ptr_to_var.*,
    });

    // and again...
    std.debug.print("\nptr_to_const: {x} = {}\nptr_to_var: {x} = {}\n\n", .{
        @intFromPtr(ptr_to_const),
        ptr_to_const.*,
        @intFromPtr(ptr_to_var),
        ptr_to_var.*,
    });
}


I apologize if this is silly or something I’m overlooking, it just seems strange to me and I don’t understand what changes were made in zig 0.15.1 or 0.15.2, I’m using zig 0.14.0.

Ouput 0.15.1 and 0.15.2

c: 
param: 7ffeadf6c3e9
const: 11981b5
var: 7ffeadf6c3ea


ptr_to_const: 11981b5 = 3
ptr_to_var: 7ffeadf6c3ea = 99


ptr_to_const: 11981b5 = 3
ptr_to_var: 7ffeadf6c3ea = 99

Ouput 0.14.0

c: 
param: 7fff76d6ad7d
const: 10217c0
var: 7fff76d6ad7f


ptr_to_const: 10217c0 = 3
ptr_to_var: 7fff76d6ad7f = 99


ptr_to_const: 10217c0 = 3
ptr_to_var: 7fff76d6ad7f = 0

Have a look at the langref section about memory.
const values that are comptime-known are stored in the read-only data section, so they live ‘forever’.
However dereferencing a pointer to a stack variable after its scope has expired is unchecked illegal behavior and always has been. What you’re seeing is likely a coincidence or the result of some implementation change, but definitely not due to the language spec changing.

3 Likes

You are returning pointers to local variables, their values should be considered garbage once the function returns, so the compiler can do whatever it wants.

My question is why do you care what the value of those variables is? You aren’t supposed to use those anymore.

1 Like

No, I’m not actually interested in the values ​​of those variables that are already dead; I just find Zig’s behavior in its different versions regarding its lifespan strange. I don’t know if I’m explaining myself well.

If you are using x86_64 as your target and compiling in debug mode, you are likely observing differences in code-generation between the self-hosted backend and the llvm backend, in newer versions debug mode on x86_64 uses the self-hosted backend by default:
https://ziglang.org/download/0.15.1/release-notes.html#x86-Backend

But that said, you are observing memory, you technically shouldn’t, so the lifespan isn’t different between the versions, because in both cases the lifespan has already ended. Basically don’t look into graves, let those dead bytes rest in peace.

With -fllvm you can opt into using llvm in debug mode, or with .use_llvm = true in the options to addExecutable.

2 Likes