The stack frame is "deleted" right before the function returns. Usually the return value is stored from the stack into a register and then the function returns. Let's look under the hood a bit to see what's really going on. I'm going to omit the actual disassembly of your code since it's a bit overwhelming, but I'll summarize the key points here. Typically a function written in C does the following (I'm using x86 Assembly as an example, the process is similar on other architectures but the register names will be different)
First, to use a function, it must be CALLed.
CALL bar
Doing so pushes the contents of the rip
register on the stack (which can be thought of as representing "what line of code we're going to run next.")
bar:
push rbp
mov rbp,rsp
Most functions written by a C compiler start out like this. The purpose of this is to create a stack frame. The contents of rbp
are stored on the stack for safekeeping. Then, we copy the value of the stack pointer (rsp
) to rbp
. The reason C does this is simple: rsp
can be altered by certain instructions such as push
,pop
,call
, and ret
, where rbp
is not. In addition, the free space above the stack that hasn't been used yet can be used by the calling function.
Next, our local variables are stored onto the stack. One of them was the 56 we passed to bar
. C chose to store the value 56 into the esi
register prior to calling the function.
mov DWORD PTR [rbp-12], esi
This basically means "take the contents of the esi
register and store them 12 bytes before the address pointed to by rbp
. This is guaranteed to be free space, thanks to the push rbp mov rbp,rsp
sequence from earlier.
Once the function does what it needs to do, the return value is stored in rax
and then we do the exit sequence.
pop rbp
ret
As for the stack frame, it wasn't actually "deleted" per se. Those values are temporarily still there until they are overwritten by another function. However, for all intents and purposes, they are considered deleted, as that stack space is now considered "free" and can be used by anything (such as hardware interrupts etc.) Therefore, after a function returns, there is no guarantee that any of its local values are still there if you try to access them. (Not that C would let you access them without inline assembly, but what I'm saying is you shouldn't even try.)