5.1.16 · D2 · HinglishC Programming

Visual walkthroughStack frames — how function calls work at the memory level

2,489 words11 min read↑ Read in English

5.1.16 · D2 · Coding › C Programming › Stack frames — how function calls work at the memory level


Step 1 — Memory sirf numbered boxes hain, aur ek number "top" yaad rakhta hai

KYA. Computer ki memory (RAM) ko ek lamba column of boxes samjho. Har box ek chhoti si value rakhta hai aur har ek ka ek address hota hai — ek whole number jo use naam deta hai. Figure mein address har box ke left side pe printed hai.

KYU. Jab tak hum "function apna saman kahaan rakhta hai" ki baat nahi kar sakte, humein kisi box ko point karne ka tarika chahiye. Address bas itna hi hai: box ka house-number. Humein ek special helper bhi chahiye jo hamesha yaad rakhe currently-used region kahan khatam hoti hai. Woh helper ek register hai (CPU ke andar ek chhota built-in storage slot) jise stack pointer kehte hain, likha jaata hai .

PICTURE. Orange arrow dekho jis pe likha hai. Yeh data ka box nahi hai — yeh ek finger hai jo us address ko point karta hai jo topmost box ka address hai aur jo hum currently use kar rahe hain.

Figure — Stack frames — how function calls work at the memory level

Step 2 — "Neeche" growth ki direction hai (yeh sabko confuse karta hai)

KYA. Har mainstream CPU (x86, ARM) pe, stack upar se shuru hota hai aur chhote addresses ki taraf grow karta hai. "Stack ko bada karna" matlab hai se subtract karna.

KYU dusri taraf nahi? Yeh ek historical convention hai taaki stack (neeche grow karta hua) aur dusri memory (upar grow karti hui) ek region share kar sakein aur sirf tab collide karein jab program sach mein memory se bahar ho. Yeh key rule tum par burn in ho jaani chahiye:

  • push: hum current top ke neeche naye boxes claim karte hain, isliye address number neeche jaata hai.
  • pop: hum boxes wapas dete hain, isliye address number upar jaata hai.

PICTURE. Do arrows: ek magenta arrow ek push dikhata hai jo ko neeche kheenchta hai (address 1000 → 992), ek violet arrow ek pop dikhata hai jo ko wapas upar jaane deta hai. Same finger, opposite directions.

Figure — Stack frames — how function calls work at the memory level

Step 3 — Ek call ko teen cheezein chahiye, isliye ek frame ke teen parts hote hain

KYA. Jab main ek function f ko call karta hai, teen sawaalon ka jawab dena padta hai. Har jawab f ke private region mein — uske stack frame mein — ek slot ban jaata hai.

KYU yeh teen? Kyunki ek call ko khud kuch aur call karne se interrupt hone ke baad bhi survive karna hai, aur saaf wapas aana hai. Teen sawaal:

Sawaal jo call ko answer karna hai Slot jo jawab deta hai
"Kaam khatam hone pe main kahaan wapas jump karun?" return address
"Main apne locals kahaan rakhu bina caller ke locals kharab kiye?" local-variable slots
"Mujhe apne locals kaise milenge jab move kare?" ==frame pointer ==

PICTURE. Ek frame, uske parts mein explode kiya gaya: return address sabse upar (frame ka highest address), phir saved old , phir neeche locals ka fresh region (lowest addresses). Ek green finger ek fixed spot pe planted hai; ek orange finger bilkul neeche baitha hai.

Figure — Stack frames — how function calls work at the memory level

Step 4 — Caller ka kaam: arguments pass karo, phir CALL karo

KYA. f mein jump karne se pehle, caller argument values push karta hai (ya ARM/x64 pe aksar registers mein load karta hai — dekho Calling conventions (cdecl, stdcall)), phir CALL f execute karta hai.

KYU do sub-steps?

  • Pehle arguments push karof ko woh values chahiye jo use di gayi hain, isliye woh kaheen honi chahiye jahan f unhe read kar sake.
  • CALL baad mein — x86 pe CALL instruction atomically do kaam karta hai: yeh return address push karta hai (us instruction ka address jo call ke theek baad aata hai) aur phir f pe jump karta hai. Yahi hai "main kahaan wapas aaun?" wale sawaal ka jawab milna.

PICTURE. Left half: main ka frame x ke saath. Do magenta pushes dekho: pehle 3 phir 2, x ke neeche land karte hain, phir ek violet push return address upar drop karta hai. teen boxes neeche crawl kar chuka hai.

Figure — Stack frames — how function calls work at the memory level

Step 5 — Callee ka prologue: BP nail karo, locals ke liye jagah nikalo

KYA. f ke andar pehle kuch instructions boilerplate hote hain jinhein prologue kehte hain. Iss order mein:

  1. push BPcaller ka frame pointer save karo taaki hum baad mein restore kar sakein.
  2. BP = SP — apna finger yahaan plant karo, is frame ka base mark karo.
  3. SP -= N ko bytes neeche slide karo taaki f ke locals ke liye room reserve ho.

KYU har line?

  • Old save karo shared hardware hai; agar hum ise save kiye bina overwrite karein, toh caller apna frame track kho dega. Isliye pehle old value stash karo.
  • — ab hamare frame ke andar ek fixed landmark mark karta hai.
  • — ek subtraction mein bytes reserve karna har local ko alag alag push karne se sasta hai, aur yeh sabhi locals ko ke relative ek constant distance pe addressable banata hai.

  • — locals ke neeche carved hue hain (lower addresses) → subtract.
  • — arguments aur return address se pehle push kiye gaye → woh upar baithe hain → add.

PICTURE. f ka poora frame. Green finger saved-old- box ko point karta hai. Uske upar, ke saath labelled, args aur return address hain; neeche, ke saath labelled, freshly carved local slots hain. Orange bilkul neeche rested hai.

Figure — Stack frames — how function calls work at the memory level

Step 6 — Worked trace: add(2,3) ek frame build aur tear down karta hai

KYA. Chalte hain ek real frame ki poori zindagi trace karte hain.

int add(int a, int b) {
    int s = a + b;   // one local: s
    return s;
}
int main(void) {
    int x = add(2, 3);
    return 0;
}

KYU trace karo? Yeh dekhne ke liye ki cleanup sirf ko wapas move karna hai — kuch individually erase nahi hota.

PICTURE. Chaar panels, left se right:

  1. main mein: stack pe sirf x.
  2. Args + CALL push karne ke baad: [x][3][2][retaddr].
  3. add ke prologue ke baad: [x][3][2][retaddr][savedBP][s], s = 2+3 = 5 computed hai.
  4. RET ke baad: bilkul upar wapas jump kar chuka hai; add ke use kiye hue chaar boxes logically gone hain; value 5 ek register mein ghar aayi aur x mein store ho gayi.
Figure — Stack frames — how function calls work at the memory level

Step 7 — Recursion: same picture, N baar stack hoti hai

KYA. Recursion koi naya mechanism nahi hai — yeh Step 5 hai jo pehle waale ke khatam hone se pehle dobara ho raha hai. Dekho Recursion.

int fact(int n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
}

KYU yeh har n "yaad" rakhta hai. Har call apna prologue run karta hai, isliye har ek ko n ke liye apna alag slot milta hai. Teen simultaneous calls ⇒ teen alag n boxes stacked up, har ek neeche waale ka wait kar raha hai.

PICTURE. fact(3) apni gehraai mein: teen frames neeche ki taraf piled, n=3 upar wait kar raha hai, n=2 neeche wait kar raha hai, n=1 sabse neeche 1 return karne wala hai pehle (deepest returns first — strict LIFO). Phir unhe upar unwind hote dekho: .

Figure — Stack frames — how function calls work at the memory level

Step 8 — Degenerate case: ek local ka pointer return karna

KYA. Dangerous edge case. Kya ho agar ek function apne kisi local ka address wapas de?

int *bad(void) {
    int local = 42;
    return &local;   // BUG
}

KYU yeh poison hai. Step 6 se humne seekha ki RET sirf ko wapas upar move karta hai — frame ke boxes erase nahi hote, lekin ab woh fair game hain next call ke liye overwrite hone ke liye. Return kiya gaya pointer un boxes ko point karta hai jo ab tumhare nahi hain. Yeh sheer luck se 42 read kar sakta hai, jab tak next function call exactly wahi boxes reuse na kare.

PICTURE. Do panels: (1) bad ke andar, pointer 42 wale box ko point karta hai. (2) return ke baad, us box ke upar chadh chuka hai, aur ek naya call other() aaya hai aur wahan 99 likh diya hai — pointer ab garbage read karta hai.

Figure — Stack frames — how function calls work at the memory level

Ek-picture summary

Upar ki saari baatein ek single frame diagram mein compress ki gayi hain: upar caller region, neeche ek poora callee frame, dono fingers ( nailed down, neeche), har slot labelled kiya gaya hai teeno sawaalon mein se kaunsa sawaal yeh answer karta hai aur kis direction mein push kiya gaya tha.

Figure — Stack frames — how function calls work at the memory level
Recall Feynman: poora walkthrough apne words mein batao

Memory ek tall stack of numbered boxes hai, aur ek finger () hamesha us top box ko point karta hai jo hum use kar rahe hain. Real chips pe "stack grow karna" matlab hai finger neeche chhote numbers ki taraf move karna. Jab ek function doosre ko call karta hai, caller argument values boxes mein daalta hai, phir CALL ek return address bhi daalta hai — ek note jo kehta hai "yahaan wapas aao." Called function phir ek doosra finger () ek fixed landmark pe plant karta hai taaki woh apne locals hamesha find kar sake, aur top finger ko neeche slide karta hai unke liye blank boxes reserve karne ke liye. Real kaam karna matlab hai un boxes ko se fixed distances pe read aur write karna. Jab woh khatam ho jaata hai, woh sirf top finger ko wapas upar slide karta hai — boxes scrub nahi hote, woh sirf abandon ho jaate hain, isliye cleanup instant hai aur isliye unka pointer return karna ek time bomb hai. Recursion yahi ritual hai jo pehle waale ke khatam hone se pehle dobara hota hai, isliye ek local ki kaafi saari copies stack up ho jaati hain, aur sabse deep wala hamesha pehle return karta hai. Yahi last-in-first-out pile call stack hai.

Recall

push pe kis direction move karta hai? ::: Neeche — ek chhote address ki taraf (x86/ARM pe). KYU locals use karte hain lekin args use karte hain? ::: Locals ke neeche carved hue the (lower addresses → subtract); args aur return address se pehle push kiye gaye the, isliye woh upar baithe hain (add). KYU &local return karna fail hota hai? ::: Return pe upar move karta hai aur frame ke boxes abandon ho jaate hain; next call unhe overwrite kar sakta hai, isliye pointer reusable/garbage memory ko point karta hai. fact(3) mein, kaunsa frame pehle return karta hai? ::: fact(1) — sabse deepest wala — strict LIFO.