Strictly speaking, the code in the question has undefined behavior because it accesses uninitialized objects. It also has a constraint violation in C99 or later, because it calls printf
with no visible declaration.
Because of the undefined behavior, the C standard says literally nothing about how the program will behave. Undefined behavior is "behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements". The standard joke is that it could make demons fly out of your nose. Of course it can't, but the point is that if it did, that would only violate physics and common sense, not the standard.
So let's get rid of the undefined behavior:
#include <stdio.h>
int main(void) {
int num = 42;
int a = num;
int b = num;
printf("%d\n", a);
printf("%d\n", b);
}
Section 6.2.4 of the ISO C standard (I'm using the N1570 draft) says:
The lifetime of an object is the portion of program execution during
which storage is guaranteed to be reserved for it. An object exists,
has a constant address, and retains its last-stored value throughout
its lifetime.
So num
, a
, and b
all have unchanging addresses throughout their lifetimes (which is the execution of the main
function), and all three addresses are distinct.
That applies to the "abstract machine". An implementation is required to produce behavior as if all that were the case. If it can do that by generating code equivalent to just puts("42\n42")
, that's a perfectly valid optimization. Or, less drastically, it could store num
, a
, and b
in the same location (perhaps a CPU register) because it can prove that they always have the same value and their addresses are irrelevant.
If the behavior of the program actually depends on the addresses of num
, a
, and b
, for example if you print the addresses using printf("%p\n", &a)
, then that restricts some optimizations. (Incidentally, taking the address of an uninitialized variable is well defined; you might pass that address to a function that initializes it, for example.)
So, if I create an application with let say 1,000 int variables that
are uninitialized, does that mean the program will use 4 byte * 1,000
when it's executed?
If you define 1000 int
variables, the compiler will generate code that will allocate sizeof (int)
bytes for each of them -- unless it can prove that it doesn't need to. If it can generate code that behaves as required without allocating that memory, it can do that. And if the behavior is undefined, then "behaves as required" isn't a requirement at all.