2
#include<stdio.h>

int* f1() { 
    int k =3; 
    return &k;
}

int main() { 
     int *z = f1(); 
     printf("%d",*z); 
}

OUTPUT is 3. I want to know when stack unbinding of f1() happens.

Jens
  • 69,818
  • 15
  • 125
  • 179
Sandeep
  • 173
  • 2
  • 3
  • 13
  • Right before the instruction pointer returns to the main function. – Thomas Jul 07 '13 at 13:05
  • 5
    This is undefined behavior. – Yu Hao Jul 07 '13 at 13:06
  • @YuHao ...because a pointer to an object of automatic storage is being used after the object's lifetime has ended. – Jens Jul 07 '13 at 13:14
  • you are returning an address of a local variable. the `k`'s lifetime end as function ends so that is undefined behavior. the compiler must be give you warning message about this. – mr5 Jul 07 '13 at 15:25

4 Answers4

7

the function f1() is returning the address of variable k but as k is on stack so after parenthesis it should unwind variable k from the stack memory

And this is exactly what the code does. But since, in your example, it does not have time to use it for something else, the value 3 remains at the address. The compiler is not going to generate code to overwrite variables that go out of scope just so that you can test that they have gone out of scope with programs such as this.

Your program invokes undefined behavior and any behavior of the generated code is thus acceptable, including displaying 3.


If you are interested in the definedness of small C examples, this freely available static analyzer has this to say about your program:

$ frama-c -val t.c
…
t.c:3:[value] warning: locals {k} escaping the scope of f1 through \result
…
t.c:8:[kernel] warning: accessing left-value z that contains escaping addresses

The first warning is only informative: the analyzer considers it okay for a function to let addresses of local variables escape their scope. In the case of an address returned as result, it is debatable whether it should not directly be considered a programming error. But not considering this error as serious and only emitting an informative message makes sense for an address written into a global variable: this practice is safe if the program subsequently avoids using the address stored in the global variable.

The second warning indicates the undefined behavior, because the indeterminate address in z is used.

Jens
  • 69,818
  • 15
  • 125
  • 179
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
1

I'm not sure I understand what you mean by stack unbinding. The point is that even though the stack frame for your call to f1() isn't valid anymore when you call printf(), it is unlikely to be overwritten and so your program gives the (wrongly) expected result.

You could make an experiment and change your program as follows:

#include<stdio.h>

int* f1() { 
  int k =3; 
  return &k;
}

int* f2() { 
  int k = 4; 
  return &k;
}

int main() { 
  int *z = f1(); 
  int *y = f2(); 
  printf("%d %d",*z, *y); 
}

What happens? Note that whatever behaviour you see, it's just the way your compiler works and there's no guarantee that another compiler will do the same.

Nicola Musatti
  • 17,834
  • 2
  • 46
  • 55
1

Forget all your notions about terms like stack, unwind and unbind. In terms of the C language, this program invokes undefined behavior because a pointer to an object of automatic storage (aka "local variable") is being used after the object's lifetime has ended (at the } of the enclosing block, which happens to be the function's closing }.)

Jens
  • 69,818
  • 15
  • 125
  • 179
0

The generated machine code might look as follows: On returning from the function, the head-of-stack pointer is decremented (or incremented, if the stack grows from high to low addresses) by the size of variables and other information required by f1. Everything required by the calling function (main) has been allocated already, so no additional space on the stack is required, so the memory where k lived has not been overwritten when you dereference the return value delivered by f1. Hence, dereferencing still gives you the value 3. The address you access is, however, not part of the stack anymore.

stack within f1                            after returning from f1

       | ...            |                  | ...            |
SP---> | 3              | k                | 3              |
       | return address |                  | return address |
       | ...            |             SP-->| ...            |

SP points to the top of the stack. Returning from the function probably simply changes SP. Everything above SP can be considered as unused, but probably is not overwritten by e.g. zeros, so it may contain "old" content.

JohnB
  • 13,315
  • 4
  • 38
  • 65