(I deal more with code generated by Visual C++, so some parts of this answer are just educated guesses.)
1.Why these 3 addresses of variables are not consecutive when allocated?
Variable alignment and even presence on the stack is up to the compiler and can differ based on the specific compiler version you use, the optimization options being used, and other factors.
The 3 variables in your example are not all "real" variables.
var_4 is the variable corresponding to your
var_20 are simply results of GCC's approach to passing arguments to deeper function calls. When you write
scanf("%d", &x);, GCC knows that it will need to pass two 4-byte variables to that function on the stack, so it pre-emptively reserves enough room for them upon entering the function. This way, it doesn't need to
push anything onto the stack (which might be problematic if there's no stack space left...), it just needs to
mov the arguments into that preallocated space.
That doesn't explain why there's a gap between the two allocations, though. GCC also prefers to align stack allocations to 16 bytes1, and this is where I am guessing it seems that the sizes needed for "real local variables" and "space reserved for deeper function arguments" are aligned independently before being summed into the final value of "reserved stack space".
1You can control this alignment by using
2.If not, when could I speculate the number of variables generated from stack by watching the clause like
add esp, N which is often at the end of a routine? Is it related with calling convention?
As you can see in this example, that instruction does not always get generated. Its counterpart,
sub esp, N, is a much better indicator. From that you can make educated guesses about the amount/sizes of local variables.
Calling convention is not related to a function's local variables, it controls the way the arguments are passed into the function and whose responsibility it is to clean them up afterwards.
3.In this example, why compiler does not generated the
add esp, 20h with it?
The function in your example begins with
push ebp; mov ebp, esp, which saves the original values of both
leave instruction at the end does the reverse - it restores the saved values of
ebp, so there's no need to calculate anything.
ebp is also known as a frame pointer. It is possible to instruct the compiler not to generate it, in which case the original value of
esp needs to be restored using that calculation you mentioned.