Overall variable experience


I stumbled on something that made me understand the extent of variable recycling:
in a function, I have a Unicode treatment, which I transform with:

vUnicode = [_]u8{0} ** 4;
var i = utf.utf8Encode(c0,&vUnicode) catch unreachable;
                 Event.Key = kbd.char;
                 Event.Char = vUnicode[0..i];

I noticed that if the declaration of vUnicode was in a function, I lost Event.char even if it is in return Event

In fact, it was like a switch from vUnicode and when closing by function was destroying the reference to that variable and causing inaccessibility…

To overcome this problem, I put the variable at the top outside of the functions in the module.zig
there in the program
or I use the import of the module
indeed, I do not lose the value

Apart from my observation and taking into account the operation of Zig-lang
did I understand right ???


Yes, I think you are getting the right idea that the address of variables declared by a function should no longer be accessed after that function returns. This is because such variables are allocated on the stack, and after the function returns that portion of the stack is reused by other functions, so its contents are overwritten.

This concept is not Zig-specific. In general these are called automatic variables and you can read more about them here:

You can also google for “stack variables” and find many explanations, videos, etc, hopefully some in your native language (I assume it is not English, but please excuse me if I’m incorrect).

I see now that you have lots of programming experience so you probably know what an automatic variable is. So what you may have been missing about Zig is:

  1. An array such as vUnicode, declared as a local/automatic variable, is stack allocated in Zig, not heap allocated.

  2. The Zig compiler does not detect when a local variable memory address escapes its scope – for example, when it is returned by the function or is stored in the field of a longer lived struct. Zig, relies on the programmer to avoid this. If the programmer does it, bad things will happen (undefined behavior) as you experienced.

1 Like

Yes, I understand perfectly, but I used a other language where the purge was done after the settings were returned.
var x = function();
good :wink: it’s a good thing to understand how it works and to get to the top.

In this case it was a variable which it was worth in “global” and not do an “alloc” function used in the keyboard read function then a defer

int *return_lva(void)
    int x;
    return &x;

#include <stdio.h>

int main(void)
    int *xp = return_lva();
    printf("xp = %p\n", xp);

gcc (9.4.0) warns (at least) about this, even without -Wall:

$ gcc lva.c 
lva.c: In function ‘return_lva’:
lva.c:5:12: warning: function returns address of local variable [-Wreturn-local-addr]
    5 |     return &x;
      |            ^~
1 Like

I’m not sure under what conditions C can warn about escaping local addresses, but I removed the “like C” in that sentence. This discussion isn’t about C, after all.

1 Like

I would be careful with the global variable solution. Given code like this:

var buf: [4]u8 = undefined;
fn iota(start: u8) []u8 {
    var i: u8 = start;
    while (i -% start < buf.len) : (i +%= 1) {
        buf[i -% start] = i;
    return buf[0..];

test {
    const a = iota(0);
    const b = iota(1);
    try std.testing.expect(!std.mem.eql(u8, a, b));

running zig test on it will show you that it fails, because in both cases the return value points at the same global memory. In cases where you need each return value to be independent of the other, it is a good idea either to accept an std.mem.Allocator parameter in order to allocate memory for the result, or accept an output buffer.

1 Like


In fact, I did it like this module cursed:
I put this variable at the top “GLOBAL”
var vUnicode: [4]u8 = undefined;
… various functions …

Just before my function, I declare keyboard

const Keyboard = struct {
      Key: kbd,
      Char: []const u8

I have a complex function read keyboard hard:

pub const kbd = enum {....
        // get All keyboard keys allowed in terminal
     pub fn getKEY() Keyboard {
         // TODO: Check buffer size

         // initiate
         var Event: Keyboard = Keyboard{.Key = kbd.none,.Char=""};
       var i = utf.utf8Encode(c0,&vUnicode) catch unreachable;
                 Event.Key = kbd.char;
                 Event.Char = vUnicode[0..i];
        return Event ....

which returns after “Event” processing

before I was declaring the vUnicode variable in getKEY

I wish my Google friend translated well

Instead of using a global you could declare it in the enclosing function scope that defines its lifetime, main if nothing else, and then pass a pointer down to the function that needs it.

1 Like

I haven’t used pointers with Zig yet
the results:
I put inside the function getKEY:

var vUnicode: []u8 = undefined;
vUnicode = allocator. alloc(u8, 4) catch unreachable;
Event.Char = vUnicode[0..i];

return Event....


var Tkey = term.getKEY(); // résult --> Tkey.Char = "é"

Your proposal forced me to go all the way, Thank you

The variable dies once getKEY() hands over the Keyboard structure; that’s what I understood

And in this case, I fall back on my feet compared to another language

but I don’t quite understand the history of pointers
( This is an other story :grinning: )