With regard to (paraphrased):
An int
take four or eight bytes but here the difference between p
and q
is twelve.
An int
does not take four or eight bytes, that's just a common thing in many implementations. The standard specifies a minimum range but no maximum. In any case, the standard does not mandate that distinct variables are allocated on the stack in any particular order. In fact, it doesn't mandate a stack at all, it just has to behave in a certain way.
Granted arrays have to be ordered in a certain way (as you can see from the addresses of the a
elements) but p
and q
are not part of an array. And, even thought the a
element addresses are four bytes apart, even that is not mandated. An implementation is free to add padding between elements and after the last element.
Since you can't (safely) use the address of p
to do anything other than access p
itself, it's address is actually irrelevant.
In regard to your second question about good reference material, that's actually disfavoured by Stack Overflow, one of the close reasons being:
Seeking recommendations for books, tools, software libraries, and more. This question is likely to lead to opinion-based answers.
Hence I won't bother answering that bit.
However, I will give a bit of extra advice: if you really want to know what specific implementations are doing with your code, you should head on over to GodBolt and type your code in. Making some minor modifications to get it to compile cleanly:
#include <iostream>
#define USE_P 1
#define USE_Q 1
#define USE_A 1
int main() {
#if USE_P
int p = 10;
#endif
#if USE_Q
int q = 11;
#endif
#if USE_A
int a[3] = {12, 13, 14};
#endif
std::cout << "hello\n";
}
You'll see something at the start of main
that looks like this (though I added the comments):
push rbp
mov rbp, rsp
sub rsp, 32 // Stack frame size
mov DWORD PTR [rbp-4], 10 // p
mov DWORD PTR [rbp-8], 11 // q
mov DWORD PTR [rbp-20], 12 // a[0]
mov DWORD PTR [rbp-16], 13 // a[1]
mov DWORD PTR [rbp-12], 14 // a[2]
And you watch how this changes when you tell it to stop using any of those variables, in any combination. From the example I gave (and using x86-64 gcc 10.1
), it appears that the variables are placed as you expected. However, as stated earlier, this is not a requirement.