#include <stdio.h>
void main() {
{
int x;
printf("%p\n", &x);
}
{
int x;
printf("%p\n", &x);
}
}
I would think running this that it would output the same thing twice. When it declares the first variable, it increments the stack pointer, but then leaves the scope, so it decrements it, and then just repeats the process the second time, so int x
would occupy the same memory location on the stack both times.
But this isn't the case. The stack pointer is not decremented and int x
in both cases occupy different locations in the stack. In fact, the first int x
is still reachable even though its scope is gone.
#include <stdio.h>
void main() {
{
int x = 10;
printf("%p\n", &x);
}
{
int x = 25;
printf("%p\n", &x);
}
{
int x = 71;
printf("%p\n", &x);
int *p = &x;
printf("%i %i %i\n", *(p + 2), *(p + 1), *p);
}
}
Why is this? What am I misunderstanding?
The C standard does not even mention a stack. The compiler is free to optimize variables away when they are not needed. There is absolutely nothing in the C standard that implies that the printouts should be neither equal or not equal.
On my computer, this manifests itself by giving different output depending on optimization level:
$ gcc c.c
/tmp$ ./a.out
0x7ffd8733c3ac
0x7ffd8733c3a8
/tmp$ gcc c.c -O3
/tmp$ ./a.out
0x7fff4e91544c
0x7fff4e91544c
In fact, the first "int x" is still reachable even though its scope is gone.
Accessing a variable that has gone out of scope causes undefined behavior, which means that anything can happen. This includes the case where the program works as intended.
Here is the output from your second snippet with different optimizations:
/tmp$ ./a.out
0x7ffd4df94864
0x7ffd4df94860
0x7ffd4df9485c
10 25 71
/tmp$ gcc c.c -O3
/tmp$ ./a.out
0x7ffc30b4e44c
0x7ffc30b4e44c
0x7ffc30b4e44c
0 0 71
When you get different behavior depending on optimization level, that's an almost 100% sign that your program has something that is causing undefined behavior. There is a very, very small chance that you have encountered a bug in the compiler. And apart from those two reasons, I cannot think of anything else that could be the cause.
As a practical matter, when a stack does exist (as Broman's answer points out, there is no requirement for a stack to exist, although there is a requirement to support recursion) compilers will typically generate code that adjusts the stack pointer only once on function entry, and once again on function exit, even if there are sub-scopes within the function that limit individual variables' lifetimes.
This might seem bizarre if you're used to writing assembly language by hand. The most basic reason for it is, it means each variable that lives on the stack has a "stack slot" with a fixed location throughout the entire function, which gives the compiler maximum flexibility in moving machine instructions around for optimization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With