Worked examples — Stack frames — how function calls work at the memory level
5.1.16 · D3· Coding › C Programming › Stack frames — how function calls work at the memory level
Yeh page parent note on stack frames ka exhaustive worked-examples lab hai. Parent ne tumhe machinery sikhaayi thi: ek frame ek private scratch desk hota hai jisme locals, ek saved base pointer, aur ek return address hote hain, aur desks LIFO (last-in, first-out) order mein pile up hoti hain. Yahan hum us machinery ko har case class ke against test karenge — stack ke har direction, har degenerate call, sabse deep recursion, buggy pointer, aur ek exam trap.
Agar koi word neeche unfamiliar lage, toh woh parent mein build kiya gaya tha. Do terms jo tum baar baar use karoge:
- Stack pointer (SP) ::: woh register jo stack ke top (newest byte) ka address hold karta hai.
- Base pointer (BP) ::: current frame ke andar ek fixed reference address, taaki locals constant offsets par rahen chahe SP move kare.
The scenario matrix
Examples karne se pehle, chaliye list karte hain har tarah ke situation jo ek stack frame face kar sakta hai. Baad ke har example mein woh cell tagged hai jise woh cover karta hai, aur saath milke yeh poora grid fill karte hain.
| # | Case class | Ismein kya special hai | Covered by |
|---|---|---|---|
| A | Single flat call (baseline) | ek caller, ek callee, koi nesting nahi | Example 1 |
| B | Nested calls (call ke andar call) | do live frames stacked | Example 2 |
| C | Downward-growing stack, address arithmetic | BP se real offsets compute karo | Example 3 (figure) |
| D | Upward-growing stack (opposite sign) | kuch CPUs / textbooks direction flip karte hain | Example 4 (figure) |
| E | Zero-work / degenerate frame | function jisme koi locals nahi, koi args nahi | Example 5 |
| F | Deep recursion → limiting behaviour | frames pile hote hain jab tak stack overflow nahi ho jaata | Example 6 |
| G | Dangling pointer to a freed local | pointer apne frame se zyada jee jaata hai | Example 7 (figure) |
| H | Real-world word problem | real stack depth bytes mein measure karo | Example 8 |
| I | Exam-style twist (ARM link register) | return address pehle stack par nahi | Example 9 (figure) |
| J | Frame alignment / padding | total 16-byte boundary par round kiya jaata hai | Example 10 |
| K | Optimised-away frame (tail call, register args, shadow space) | conventions jo frame ko skip ya reshape karte hain | Example 11 |
Jahan bhi numbers aate hain hum stack ko explicitly bytes mein track karte hain, taaki har claim checkable ho.
Example 1 — baseline single call (cell A)
- Return address push karo.
CALL8 subtract karta hai: . Yeh step kyun? x86 parCALLstack par return address store karta hai taakifko pata ho kahan wapas jaana hai; ek push SP ko lower addresses ki taraf move karta hai. - Saved BP push karo. Prologue 8 subtract karta hai: .
Yeh step kyun?
fkomainka BP overwrite karne se pehle save karna hai, warnamainapna frame reference kho deta hai. BP = SPset karo. Ab . Yehfka anchor hai; yeh SP move nahi karta. Yeh step kyun? Locals is fixed point ke relative address honge.- 16 bytes locals carve karo. . Yeh step kyun? Local variables ko downward stack par BP ke neeche reserved space chahiye.
Answer: f ke andar, . Frame cost bytes. Frame isliye byte range span karta hai — naye top se lekar (lekin include nahi karta) wahan tak jahan main ka frame resume hota hai.
Example 2 — nested calls, do live frames (cell B)
fka frame push hua. . Yeh step kyun?fko call karnafka frame main ke (neeche) upar build karta hai.gka frame push hua. . Yeh step kyun?fkagko call karnafke neeche ek aur frame build karta hai. Ab ek saath teen frames live hain — yeh sabse deep moment hai.greturn karta hai. SP 24 se upar reset hota hai: . Yeh step kyun? Cleanup sirf SP ko wapas move karna hai;gki desk disappear ho jaati hai.freturn karta hai. ,mainke andar wapas. Yeh step kyun? LIFO —g(last created) pehle mara, phirf.
Answer: deepest ; destruction order hai g, phir f, phir main (creation ka reverse).
Example 3 — downward stack par real offset arithmetic (cell C)

local0BP se 8 neeche hai. . Toh . Yeh step kyun? BP ke baad push hone wala pehla local next lower 8-byte slot mein jaata hai.local1BP se 16 neeche hai. . Toh . Yeh step kyun? Doosra local ek word aur neeche hai.arg0BP se 16 upar hai. . Toh (beech mein saved BP ke liye 8 + return address ke liye 8). Yeh step kyun? Arguments call se pehle push hue the, isliye woh BP ke upar rehte hain; unhe reach karne ke liye saved BP aur return address step over karne padte hain.
Answer: local0 @ 492, local1 @ 484, arg0 @ 516.
Example 4 — UPWARD-growing stack (cell D)

- Return address push karo. SP 8 se increase hota hai: . Yeh step kyun? "Grows upward" matlab har push SP mein subtract ki jagah add karta hai.
- Saved BP push karo. SP aur 8 se increase hota hai: . Yeh step kyun? Growth direction tumhe caller ka BP save karne se excuse nahi karta — yeh har architecture par mandatory hai, warna caller apna frame reference kho deta hai. Ise skip karna woh flaw tha jo ek earlier draft mein tha.
BP = SP = 716set karo, phir locals upward carve karo. . Yeh step kyun? Locals ab BP ke upar rehte hain, toh formula par flip ho jaata hai.local0par hai,local1par. Yeh step kyun? Kyunki growth upward hai, higher offset = higher address.
Answer: ; locals BP ke upar hain; address formula mein sign + par flip ho jaata hai; frame cost bytes — Example 1 jaisi total, sirf direction reverse ho gayi.
Example 5 — degenerate frame: koi args nahi, koi locals nahi (cell E)
CALLphir bhi 8-byte return address push karta hai. . Yeh step kyun? Ek no-op ko bhi pata hona chahiye kahan wapas jaana hai — return address non-optional hai.- Prologue omit ho sakta hai. Ek smart compiler BP save karna aur locals carve karna skip kar deta hai kyunki anchor karne ke liye kuch hai hi nahi. Yeh step kyun? Koi locals nahi ⇒ stable BP reference ki zaroorat nahi ⇒ prologue pure overhead hai aur remove ho jaata hai (yahi "frame-pointer omission" hai).
RETreturn address pop karta hai. , fully restored. Yeh step kyun? Single push single pop se undo hota hai.
Answer: minimum stack change 8 bytes hai (sirf return address); BP ki zaroorat nahi.
Example 6 — deep recursion aur overflow limit (cell F)
- Har level 512 bytes cost karta hai aur mid-recursion mein koi free nahi hota. Deepest point par saare frames simultaneously live hote hain. Yeh step kyun? Recursion ek frame tab tak free nahi karti jab tak uski call return nahi hoti; maximum depth par har frame abhi bhi live hai.
- Total ko per-frame cost se divide karo. levels. Yeh step kyun? Total budget ÷ cost per frame = kitne fit hote hain region khatam hone se pehle.
- Level 16385 boundary ke baad push karta hai → stack overflow. Yeh step kyun? Jab SP guard page cross karta hai, OS fault karta hai; parent ne ise "too deep ⇒ stack overflow" kaha tha.
Answer: lagbhag 16384 levels; agli wali overflow karta hai.
Example 7 — freed local par dangling pointer (cell G)

badke return karne ke baad, SP 1000 par wapas chala jaata hai. Poora range — address 976 sameta — ab reserved nahi raha. Yeh step kyun? Cleanup = SP ko wahan reset karna jahan woh call se pehle tha;badka poora 32-byte frame logically destroy ho jaata hai jaise hi woh return karta hai, sirf ek part nahi.pabhi bhi 976 point karta hai. Pointer value unchanged hai — yeh sirf unowned memory ki taraf aim karta hai ab. Yeh step kyun?&localreturn karna address copy karta hai, aur address survive karta hai chahe storage nahi karta.gko call karna same region mein 32-byte frame rebuild karta hai. Kyunkigse start karta hai aur 8 (retaddr) + 8 (saved BP) + 16 (locals) = 32 bytes push karta hai, uska frame exactly un addresses par land karta hai jobadne abhi khali kiye — address 976 abgka hai. Yeh step kyun? Stack same low addresses next frame ke liye recycle karta hai; same size ⇒ same footprint ⇒ 976 par guaranteed collision.*pab woh kuch read karta hai jogne 976 par likha — ek saved BP byte yagka local, 42 nahi. Yeh step kyun? 42 overwrite ho gaya; ise read karna undefined behaviour hai — yeh 42 read kar sakta hai luck se before kisi call ke, garbage after.
Answer: *p undefined hai; commonly 42 return ke immediately baad (bytes untouched), phir garbage jab g ka 32-byte frame reuse karta hai aur 976 overwrite karta hai. Fix: value se return karo ya malloc (heap) use karo.
Example 8 — real-world word problem: depth bytes mein measure karna (cell H)
- Dono stack pointers ko plain numbers ki tarah read karo. hai "top of main", hai "deepest point". Yeh step kyun? "Kitna deep" ek distance hai do addresses ke beech, aur distance ek subtraction hai — toh hume dono SP values ko ordinary integers ki tarah treat karna hai jo hum subtract kar sakein, opaque labels ki tarah nahi.
- Notice karo ki dono addresses saare high bits share karte hain. Dono
0x7fffffffe0__hain; woh sirf final teen hex digits mein differ karte hain,200versus040. Kyunki high halves identical hain, unhe subtract karne par wahan zero milega, aur koi borrow us shared prefix mein climb nahi kar sakta. Yeh step kyun? Jab do numbers apne leading bits par agree karte hain, saara difference low bits mein rehta hai — toh hum sirf differing tails subtract kar sakte hain aur safely identical prefix ignore kar sakte hain. - Downward stack par top address bada hota hai, toh depth = top − deep. Tails compute karo: .
Yeh step kyun? Stack
mainse downward grow hua, toh used bytes deep (chhote) address se main ke (bade) start tak stretch karte hain; positive difference us span mein bytes ki count hai. - ko decimal mein convert karo. . Yeh step kyun? Hex base-16 hai; har digit 16 ki power se weighted hai, aur 12 ka hex digit hai.
Answer: call chain us moment mein 448 bytes (0x1c0) deep hai.
Example 9 — exam twist: ARM link register (cell I)

BL ggka return address LR mein daalta hai. Stack abhi tak unchanged: . Yeh step kyun? ARM ka branch-and-link ek register write hai, push nahi — x86 ke memory push se sasta, toh return address ka pehla hop ek register hai, stack nahi.gabhiBL hkarne wala hai, jo LR overwrite karega. Tohgpehle apna LR stack par push (spill) karta hai: . Yeh step kyun?BL hLR kohke return address se overwrite karega. Agargne LR pehle save nahi kiya, tohhko call karnagka apna return address destroy kar deta aurgkabhifpar wapas nahi ja sakta. Stack par spill karna use park karta hai jahanBL htouch nahi kar sakta.grun karta hai, phir return se pehle LR restore karta hai spilled value wapas pop karke: . Yeh step kyun?gko apna return address LR mein (yaRETke liye stack par) chahiyefpar wapas jaane ke liye; epilogue spill undo karta hai, aur SP wahan wapas jaata hai jahan woh tha.- Ek leaf function (jo kisi ko call nahi karta) steps 2–3 skip karta hai. Woh apna return address poore time LR mein rakhta hai aur iske liye kabhi stack touch nahi karta.
Yeh step kyun? Ek leaf kabhi doosra
BLexecute nahi karta, toh LR kabhi clobber nahi hota — protect karne ke liye kuch nahi, toh koi spill needed nahi. Exactly yahi reason hai ki leaf functions ARM par saste hote hain.
Answer: spill ke baad, (aur matching restore ke baad wapas); spill sirf non-leaf functions ke liye mandatory hai, kyunki sirf woh ek inner BL issue karte hain jo LR overwrite karta.
Example 10 — frame alignment aur padding (cell J)
- Local size ko next 16-byte multiple par round up karo. (kyunki ). Extra bytes padding hain — unused filler.
Yeh step kyun? Convention demand karta hai ki SP 16-aligned rahe taaki SIMD instructions (jo aligned memory require karte hain) aur next
callcorrectly kaam kare. 16 ka non-multiple carve karna SP ko misalign kar deta. - Rounded size subtract karo. . Yeh step kyun? Downward stack ⇒ locals carve karna subtract karta hai; hum padded 32 subtract karte hain, raw 20 nahi.
- Alignment check karo. exactly, toh 16-aligned hai. ✓
Yeh step kyun? Confirm karta hai ki padding ne apna kaam kiya — SP ek 16-byte boundary par land kiya, next
callke liye ready.
Answer: compiler 32 bytes subtract karta hai (20 needed + 12 padding); final , still 16-aligned.
Example 11 — frames jo optimise away ya reshape ho jaate hain (cell K)
- (a) Tail call
fka frame reuse karta hai new frame stack karne ki jagah. Kyunkifgke return karne ke baad kuch nahi karta, uska frame dead weight hai; TCOfka frame pehle tear down karta hai, phirgmein jump karta hai taakigsame space reuse kare. Yeh step kyun? Agar call ke baadfmein kuch run nahi hota, tohgke dauranfke locals aur saved BP useless hain — unhe rakhna sirf depth pile karega. TCO call ko ek jump mein convert karta hai, toh net depth added = 0 aur infinite tail recursion overflow nahi kar sakta (Example 6 ke unlike). Non-tail recursion mein aisa escape nahi hota. - (b) Register args stack bytes zero cost karte hain arguments ke liye. Saare 4 ints registers mein hone se koi arguments push nahi hote; agar
gke koi locals bhi nahi hain aur yeh leaf hai, toh uska poora frame sirf 8-byte return address hai. Yeh step kyun? System V convention (dekho Calling conventions (cdecl, stdcall)) pehle kai integer args registers mein isliye pass karta hai taaki stack traffic avoid ho — toh parent ke diagram ka "push arguments" step simply vanish ho jaata hai. Frame cost yahan = 8 bytes, same minimum jaise degenerate Example 5. - (c) Windows shadow space 32 extra caller bytes force karta hai. Chahe 4 args registers mein hon, caller ko abhi bhi 32 bytes subtract karne padte hain taaki callee ke paas un register args ko spill karne ki scratch room ho agar chahe. Yeh step kyun? Different conventions alag trade-offs karte hain: Microsoft x64 debuggability aur uniform arg handling ke liye yeh reserved slot mandate karta hai, toh wahan ek call 32 (shadow) + 8 (return address) = 40 bytes minimum cost karta hai — System V ke 8 se zyada, exact same source code ke liye.
Answer: (a) net 0 bytes (frame reused); (b) 8 bytes (sirf return address); (c) 40 bytes minimum (32 shadow + 8 return address). Same call, teen depths, convention par depend karta hai.
Recall Self-test: har scenario ke liye cell name bolo
Ek saath do live frames ::: cell B (nested calls) Ek function jisme koi args aur locals nahi ::: cell E (degenerate frame) Return address pehle register mein rakha ::: cell I (ARM link register) Pointer jo apne frame se zyada jeeta hai ::: cell G (dangling pointer) Frames pile hote hain jab tak region khatam nahi ho jaata ::: cell F (deep recursion / overflow) Stack jahan ek push SP increase karta hai ::: cell D (upward-growing) Frame size 16-byte boundary par round up hoti hai ::: cell J (alignment / padding) Tail call jo zero stack depth add karta hai ::: cell K (optimised-away frame)