#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.
#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.
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.
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.
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 }
.)
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.