16

In most managed languages (that is, the ones with a GC), local variables that go out of scope are inaccessible and have a higher GC-priority (hence, they'll be freed first).

Now, C is not a managed language, what happens to variables that go out of scope here?

I created a small test-case in C:

#include <stdio.h>
int main(void){
    int *ptr;

    {
        // New scope
        int tmp = 17;
        ptr = &tmp; // Just to see if the memory is cleared
    }

    //printf("tmp = %d", tmp); // Compile-time error (as expected)
    printf("ptr = %d\n", *ptr);

    return 0;
}

I'm using GCC 4.7.3 to compile and the program above prints 17, why? And when/under what circumstances will the local variables be freed?

Lukas Knuth
  • 25,449
  • 15
  • 83
  • 111
  • `gcc` `4.7.3` has not been released as of today. This must be a `4.7.3` pre-release. – ouah Dec 15 '12 at 01:07

3 Answers3

20

The actual behavior of your code sample is determined by two primary factors: 1) the behavior is undefined by the language, 2) an optimizing compiler will generate machine code that does not physically match your C code.

For example, despite the fact that the behavior is undefined, GCC can (and will) easily optimize your code to a mere

printf("ptr = %d\n", 17);

which means that the output you see has very little to do with what happens to any variables in your code.

If you want the behavior of your code to better reflect what happens physically, you should declare your pointers volatile. The behavior will still be undefined, but at least it will restrict some optimizations.

Now, as to what happens to local variables when they go out of scope. Nothing physical happens. A typical implementation will allocate enough space in the program stack to store all variables at the deepest level of block nesting in the current function. This space is typically allocated in the stack in one shot at the function startup and released back at the function exit.

That means that the memory formerly occupied by tmp continues to remain reserved in the stack until the function exits. That also means that the same stack space can (and will) be reused by different variables having approximately the same level of "locality depth" in sibling blocks. The space will hold the value of the last variable until some other variable declared in some sibling block variable overrides it. In your example nobody overrides the space formerly occupied by tmp, so you will typically see the value 17 survive intact in that memory.

However, if you do this

int main(void) {
  volatile int *ptr;
  volatile int *ptrd;

  { // Block
    int tmp = 17;
    ptr = &tmp; // Just to see if the memory is cleared
  }

  { // Sibling block
    int d = 5;
    ptrd = &d;
  }

  printf("ptr = %d %d\n", *ptr, *ptrd);
  printf("%p %p\n", ptr, ptrd);
}

you will see that the space formerly occupied by tmp has been reused for d and its former value has been overriden. The second printf will typically output the same pointer value for both pointers.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
5

The lifetime of an automatic object ends at the end of the block where it is declared.

Accessing an object outside of its lifetime is undefined behavior in C.

(C99, 6.2.4p2) "If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime."

ouah
  • 142,963
  • 15
  • 272
  • 331
4

Local variables are allocated on the stack. They are not "freed" in the sense you think about GC languages, or memory allocated on the heap. They simply go out of scope, and for builtin types the code won't do anything - and for objects, the destructor is called.

Accessing them beyond their scope is Undefined Behaviour. You were just lucky, as no other code has overwritten that memory area...yet.

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176