Barring alternate memory managers (if you use one, you know the diagram and are now writing a post why it's wrong), stack grows down, heap grows up. When you call a function, SP is decremented by the total size of the stack variables in the called function, the address of the next instruction in the current function is written at SP, and each variable in the called function is written at SP - x, where x is an offset calculated by the compiler. When the function returns, the memory isn't cleared, the address of where we left off the caller is read, SP is incremented to its previous value, and the processor resumes from that point. The "push" and "pop" cpu instructions don't allocate memory, they're just a shorthand to decrement SP and copy a value.
For a fun demonstration, compile this C code with -O0 (optimizations off, or debug build in Visual Studio, IIRC):
#include <stdio.h>
int foo(int unused) {
int a;
return a;
}
int bar(int x) {
int b;
b = x;
return b;
}
int main(int argc, char** argv) {
printf("%d\n", foo());
bar(10);
printf("%d\n", foo());
return 0;
}
For a fun demonstration, compile this C code with -O0 (optimizations off, or debug build in Visual Studio, IIRC):