I'll assume you're in some function since you're calling functions and such.
The space for both test
and x
is allocated on the stack. In theory, the space for these guys should exist before their values are filled in. If we look at the generated assembly (x86 gcc), this is true.
subl $40, %esp # Add 40 bytes of memory to the current stack
movl $0, -20(%ebp) # Clear test[0] to 0
movl $0, -16(%ebp) # Clear test[1] to 0
movl $45, -20(%ebp) # Place the value of 45 into test[0]
movl -20(%ebp), %eax # Copy that 45 into a register
movl %eax, -16(%ebp) # Move that register's value (45) into test[1]
movl $111, -12(%ebp) # Assign x to be 111, optimize out the unnecessary duplicate assignment
... #continues on to set up and call printf
We can see that 40 bytes are added to the stack. Notice how the addresses of test[0], test[1], and x are all contiguous addresses marked off from %ebp
at 4 byte intervals (-20,-16,-12, respectively). Their locations in memory exist and can be accessed without error before they are defined. The compiler here clears them both to 0, although we can see that this is unnecessary. You can delete those two lines and still run fine.
What we can draw from this is that your int test[2] and int x could have any number of funky circular references within themselves and the code will compile - it's just your job to make sure your references grab good data (ie somehow initialized data) and not garbage, which you've done here. This also works with other cases - compile to assembly and check it out for yourself how it's done.