When you ask questions like this, you should be clear whether you are asking about C semantics or about program implementation.
C semantics are described using a model of an abstract computer in which all operations are performed as the C standard describes them. When a compiler compiles a program, it can change how the program is implemented as long as it gets the same results. (The results that must be correct are the observable behavior of the program: its output, including data written to files, its input/output interactions, and its accesses to volatile objects.)
In the abstract computer, memory for x
is reserved from the time an execution of foo
starts until that execution of foo
ends.1, 2
So, in the abstract computer, it does not matter if x
is used or not; memory is reserved for it until foo
returns or its execution is ended in some other way (such as a longjmp
or program termination).
When the compiler implements this program, it is allowed optimize away x
completely (if it and its address are not used in any way that requires the memory to be reserved) or to use the same memory for x
that it uses for other things, as long as the uses do not conflict in ways that change the observable behavior. For example, if we have this code:
int x;
int *y = &x;
x = 3;
printf("%d\n", x);
int b = 4;
printf("%d\n", b);
then the compiler may use the same memory for b
that it uses for x
.
On the other hand, if we have this code:
int x;
int *y = x;
printf("%p\n", (void *) y);
int b = 4;
printf("%p\n", (void *) &b);
then the program must print different values for the two printf
statements. This is because different objects that both exist at the same moment in the abstract computer model must have different addresses. The abstract computer would print different addresses for these, so the compiler must generate a program that is faithful to that model.
Footnotes
1 There can be multiple executions of a function live at one time, due to nested function calls.
2 Sometimes people say the lifetime of x
is the scope of the function, but this is incorrect. The function could call another routine and pass it y
, which has the address of x
. Then the other routine can access x
using this address. The memory is still reserved for x
even though it is not in the scope of the other routine’s source code. During the subroutine call, the execution of foo
is temporarily suspended, but it is not ended, so the lifetime of x
has not ended.