The exact steps depend on the platform, but conceptually every call does:
CALLER: CALLEE (the called function):
1. push arguments 4. push old BP (save caller's frame ref)
(or load into registers) 5. BP = SP (BP now marks this frame's base)
2. CALL f -> saves the 6. SP -= N (carve N bytes for local vars)
return address 7. ... do work, locals at [BP - offset] ...
3. (execution jumps to f) 8. SP = BP (drop locals)
9. pop old BP (restore caller's BP)
10. RET -> uses return address, jumps back
int add(int a, int b) { // frame: a, b, return addr, saved BP int s = a + b; // local s return s;}int main(void) { int x = add(2, 3); // main's frame holds x return 0;}
Trace (downward stack, x86-style where return addr is pushed):
Event
Stack (top = bottom of table)
in main
[ x ]
push args
[ x ][ 3 ][ 2 ] — Why? callee needs values
CALL add
[ x ][3][2][ retaddr ] — Why? so add knows where to return
add prologue
[ x ][3][2][retaddr][ saved BP ][ s ] — Why? save caller frame + carve local
add returns 5
pop everything add added → SP back to after args; result 5 in a register
int *bad(void) { int local = 42; return &local; // BUG: returns address of a local}
Recall Feynman: explain to a 12-year-old
Imagine doing homework. When you start a worksheet you grab a fresh sheet of scratch paper and write
"after this, go back to page 5." You scribble your rough work on it. If that worksheet tells you to do
a mini worksheet, you grab another scratch sheet on top. When you finish the mini one, you crumple
its sheet and go back to the one underneath. The pile of scratch sheets is the stack, each sheet is
a frame, and the "go back to page 5" note is the return address. You always finish and crumple
the top sheet first.
The block of stack memory for ONE active function call: its locals, saved registers, return address, and often its arguments.
How does the return address get saved on x86 vs ARM?
x86 CALL pushes it onto the stack; ARM BL (branch-and-link) stores it in the link register (LR), spilling LR to the stack only if the function calls another.
On x86/ARM, does the stack grow toward higher or lower addresses?
Lower addresses (each push decreases the stack pointer).
Why is the frame/base pointer (BP) needed when we already have SP?
SP moves during execution (e.g. pushing args); BP stays fixed so locals can be addressed at constant offsets like [BP-8].
Why must a callee save and restore the caller's BP?
BP is a shared register; saving/restoring it lets each frame anchor its own locals without losing the caller's frame.
Why is returning &local from a function a bug?
The local lives in a frame destroyed on return; the pointer dangles into reclaimed memory (undefined behavior).
Why does recursion keep separate copies of each variable?
Each call builds a fresh frame, so each call's locals occupy distinct memory.
What causes a stack overflow?
Too many nested/recursive frames (or huge locals) exhausting the stack region before frames are freed.
What discipline frees frames, and how cheap is it?
LIFO; freeing is just resetting SP back — O(1), no per-variable deletion.
Where do arguments sit relative to BP vs locals (downward stack)?
Args/return-address are ABOVE BP (BP+offset); locals are BELOW BP (BP-offset).
Socho jab tum koi function call karte ho, to computer ek chhota sa "scratch desk" banata hai us
function ke liye — yahi hai stack frame. Is desk pe rehte hain: function ke local variables,
arguments, aur sabse important return address (matlab "kaam khatam hone ke baad wapas kahaan
jaana hai"). Jaise hi function return karta hai, uska desk turant hata diya jaata hai. Kyunki calls
nested hoti hain (main → f → g), ye desks ek dusre ke upar pile ho jaate hain — LIFO style, jo
sabse baad mein bana wahi sabse pehle hatta hai. Isi pile ko call stack kehte hain.
Ek important detail: return address kahaan save hota hai, ye architecture pe depend karta hai.
x86 pe CALL instruction return address ko stack pe push kar deta hai. Lekin ARM pe BL
(branch-and-link) us address ko ek link register (LR) mein daalta hai, stack pe nahi — aur agar
wo function aage koi aur function call kare (jisse LR overwrite ho jaaye), tabhi prologue LR ko stack
pe spill karta hai. Stack pointer (SP) stack ke top ko point karta hai, aur base pointer (BP)
current frame ke andar ek fixed reference deta hai taaki locals ko BP-8 jaise constant offset se
access kar sako, chahe SP idhar-udhar move karta rahe. Aur yaad rakho: zyaadatar CPUs pe stack
neeche ki taraf grow karta hai (push pe address kam hota hai).
Do cheezein zaroor yaad rakho. Pehla: recursion mein har call ka apna alag frame hota hai, isliye
har n ki alag copy hoti hai — bahut deep recursion = stack overflow. Doosra (bahut common bug):
kabhi bhi local variable ka address return &local mat karo, kyunki return hote hi wo frame destroy
ho jaata hai aur pointer ek dead memory ko point karta reh jaata hai. Ye samajhna interviews aur
debugging dono mein game-changer hai — kyunki ab tum exactly bata sakte ho ki "ye variable kahaan,
kab tak, aur kyun zinda rehta hai."