5.1.14 · D4 · HinglishC Programming

ExercisesDynamic memory — malloc, calloc, realloc, free

2,921 words13 min read↑ Read in English

5.1.14 · D4 · Coding › C Programming › Dynamic memory — malloc, calloc, realloc, free

Yeh page parent topic ke liye ek self-testing ladder hai. Har problem L1 (Recognition) se lekar L5 (Mastery) tak graded hai. Problem padho, pehle paper par try karo, phir collapsible solution kholo. Har level ke baad ek [!mistake] callout hai jo us level par students ka sabse common trap dissect karta hai.

Shuru karne se pehle, do quick reminders jo har solution mein kaam aate hain:

Is page ke saare numeric answers ke liye assumptions (taaki results checkable hon): sizeof(int) = 4, sizeof(char) = 1, sizeof(double) = 8, aur pointers/size_t 8 bytes hain (ek typical 64-bit machine).


Level 1 — Recognition

Goal: kya tum ek call padh ke bata sakte ho ki woh kya karta hai?

Exercise 1.1

Upar diye gaye sizes ko assume karte hue, har call kitne bytes ki memory request karta hai, state karo.

int    *a = malloc(10 * sizeof(*a));
char   *b = calloc(32, sizeof(*b));
double *c = malloc(6 * sizeof(*c));
Recall Solution 1.1

ko har term par apply karo.

  • a: sizeof(*a) = sizeof(int) = 4, toh bytes.
  • b: calloc(32, sizeof(char)) = bytes, aur sab zero set hain.
  • c: sizeof(*c) = sizeof(double) = 8, toh bytes.

Contents note karo: a aur c garbage hain (uninitialized), b all zeros hai kyunki woh calloc se aaya hai.

Exercise 1.2

Har blank ke liye, woh ek function batao jo fit hota hai:

  1. "Bytes reserve karo, unhe clear karne ki zaroorat nahi." ⟶ ______
  2. "Bytes reserve karo aur sabko zero set karo." ⟶ ______
  3. "Existing block ko bada karo, purana data rakhho." ⟶ ______
  4. "Ek block heap ko wapas do." ⟶ ______
Recall Solution 1.2
  1. malloc (fast, dirty memory)
  2. calloc (cleared allocate)
  3. realloc (resize, contents preserve karte hue)
  4. free (return to the heap)

Recognition drill:

calloc woh kya karta hai jo malloc nahi karta?
Woh memory zero karta hai aur tumhare liye count × size ka multiplication karta hai.
free(p) ke baad, p mein abhi bhi kya value hoti hai?
Wahi address jo pehle tha — free tumhara pointer change nahi karta.

Level 2 — Application

Goal: kya tum ek correct, checked allocation likh sakte ho?

Exercise 2.1

Code likho jo n doubles ke liye room allocate kare (jahan n sirf runtime par pata hota hai), element i ko i / 2.0 se fill kare, phir use correctly free kare. Parent note jo do safety steps insist karta hai woh include karo.

Recall Solution 2.1
#include <stdlib.h>
 
double *d = malloc(n * sizeof(*d));   // 1. bytes = n * sizeof(double)
if (d == NULL) return 1;              // 2. malloc fail ho sakta hai -> check karna zaroori hai
for (int i = 0; i < n; i++)           // 3. malloc garbage deta hai -> initialize karo
    d[i] = i / 2.0;
free(d);                              // 4. memory leak avoid karo
d = NULL;                             // 5. dangling pointer avoid karo

Do "safety steps" hain NULL check (step 2) aur free ke baad d = NULL (step 5). Step 2 skip karne se NULL dereference ka risk hai; step 5 skip karne se ek dangling pointer rah jaata hai jise baad mein free(d) ya d[0] undefined behavior mein badal deta.

Exercise 2.2

Tumhe 100 frequency counters chahiye, sab 0 se start hote hue. Sahi function use karte hue ek-line allocation likho, aur batao ki kul kitne bytes request karta hai.

Recall Solution 2.2
int *freq = calloc(100, sizeof(*freq));
if (!freq) return 1;

calloc correct hai kyunki counters zero se start hone chahiye — malloc ke saath tumhe memset ya loop add karna padta. Kul bytes: bytes, sab zeroed.

Application drill:

malloc ka return use karne se pehle kyun check karo?
Agar heap exhausted ho toh woh NULL return karta hai; NULL use karna undefined behavior hai.
calloc counters ke liye natural choice kyun hai?
Counters ko zero se start karna padta hai, jo calloc guarantee karta hai.

Level 3 — Analysis

Goal: kya tum bug dhundh ke failure explain kar sakte ho?

Exercise 3.1

Yeh code compile hota hai aur aksar "work" karta hai. Bug identify karo aur concrete failure mode describe karo.

int *p = malloc(3 * sizeof(*p));
free(p);
p[0] = 7;           // <-- ?
Recall Solution 3.1

Bug use-after-free hai. free(p) ke baad, teen ints heap allocator ke paas wapas chali gayi hain; p abhi bhi purana address hold karta hai (uske bits nahi badle), toh p[0] = 7 us memory mein likhta hai jis program ka ab ownership nahi hai.

Failure mode: undefined behavior. Allocator shayad already us block ko kisi doosre allocation ko de chuka ho, toh tumhari write kisi aur ke data ko — ya allocator ke apne bookkeeping ko — silently corrupt kar deti hai — ek crash ya ek bug produce karta hai jo bahut door surface karta hai. Fix: free(p); p = NULL; — tab p[0] = 7 silent corruption ki jagah ek clean NULL-dereference crash ban jaata hai. Valgrind jaisa tool isse instantly flag karta hai.

Exercise 3.2

"Old block ka owner kaun hai" ke terms mein explain karo ki neeche ki line mein realloc fail hone par memory kyun leak hoti hai.

v = realloc(v, cap * sizeof(*v));   // v ko grow karo
Recall Solution 3.2

Failure par, realloc NULL return karta hai lekin old block ko untouched aur valid chhod deta hai. Assignment v ko NULL se overwrite kar deta hai, toh old (still-allocated) block ka address ab hamesha ke liye kho gaya — koi bhi use hold nahi karta, toh use kabhi free nahi kiya ja sakta. Woh memory leak hai.

Fix — ek temporary use karo, sirf success par commit karo:

int *tmp = realloc(v, cap * sizeof(*v));
if (!tmp) { free(v); return 1; }   // purana v abhi valid hai -> free karo, ya rakhho
v = tmp;                           // sirf tab commit karo jab pata ho ki kaam kiya

Analysis drill:

Failure par, kya realloc old block free karta hai?
Nahi — old block valid rehta hai; yahi exact wajah hai ki self-assign leak karta hai.
Kaunsa tool use-after-free aur leaks runtime par pakad leta hai?
Valgrind.

Level 4 — Synthesis

Goal: kya tum growth, copying, aur safety ko ek sahi routine mein combine kar sakte ho?

Exercise 4.1

Neeche diye geometric-growth buffer ko trace karo. cap = 2 se shuru karte hue, x = 0, 1, ..., 9 (10 pushes) push karte waqt har grow hone ke moment cap ki value list karo. Kitni baar grow hoti hai, aur final cap kya hai?

int cap = 2, len = 0;
int *v = malloc(cap * sizeof(*v));
for (int x = 0; x < 10; x++) {
    if (len == cap) {          // full?
        cap *= 2;              // double
        int *tmp = realloc(v, cap * sizeof(*v));
        if (!tmp) { free(v); return 1; }
        v = tmp;
    }
    v[len++] = x;
}
free(v);

Growth ko ek staircase ki tarah dekhna sabse aasaan hai — Figure s01 dekho, jahan har blue step ek doubling hai aur orange line dikhati hai kitne items store kiye hain.

Figure — Dynamic memory — malloc, calloc, realloc, free
Recall Solution 4.1

Hum sirf tab grow karte hain jab len == cap (buffer exactly full ho), aur har grow cap ko double karta hai.

push x se pehle len cap full? grow to
0 0 2 nahi
1 1 2 nahi
2 2 2 haan → 4
3 3 4 nahi
4 4 4 haan → 8
5–7 5,6,7 8 nahi
8 8 8 haan → 16
9 9 16 nahi

Grows len = 2, 4, 8 par hoti hain, yani 3 grows, aur cap jaata hai 2 → 4 → 8 → 16. Final cap = 16.

Exercise 4.2

Doubling amortized total copying kyun deta hai, jabki "har baar +1 grow karo" kyun deta hai? cap = 1 se len = 8 tak pahunchne ke liye har strategy kitne element-copies perform karti hai, woh number do.

Recall Solution 4.2

Jab realloc block move karta hai, usse old elements ko nayi jagah copy karna padta hai. Cost in copies se dominate hoti hai.

Doubling (1 → 2 → 4 → 8): grows copy karte hain 1 + 2 + 4 = 7 elements total (har baar double karte waqt current contents copy hote hain). Generally sum hota hai, jo hai — pushes mein spread karo, toh per push amortized.

+1 grow karo (1 → 2 → 3 → ... → 8): 7 grows mein se har ek saare current elements copy karta hai: copies. Generally hota hai, jo hai.

Toh doubling: 7 copies; +1 growth: 28 copies — aur jaise jaise badhta hai yeh gap tezi se barhta hai.

Synthesis drill:

len aur cap ko alag-alag variables kyun rakhte hain?
cap allocated space hai, len used space hai; tum sirf tab grow karte ho jab used, allocated ke barabar ho jaaye.
Geometric growth overall sasta kyun hota hai?
Total copying ki jagah amortized hoti hai.

Level 5 — Mastery

Goal: kya tum overflow, portability, aur guarantees ki exact wording ke baare mein reason kar sakte ho?

Exercise 5.1

Ek student malloc(count * sizeof(*p)) likhta hai jahan count ek size_t hai jo user control karta hai. Ek 64-bit machine par (size_t max ), sizeof(*p) = 8 ke saath, sabse chhota count kya hai jiske liye count * 16 size_t mein overflow hota hai, aur security danger explain karo.

Recall Solution 5.1

size_t overflow ka matlab hai count * 16 se zyada ho jaata hai aur modulo wrap around ho jaata hai. Product pehli baar max exceed karta hai jab Toh sabse chhota overflowing count hai. Us value par, count * 16 = 2^{64}, jo wrap hokar 0 ban jaata hai.

Danger: malloc(0) ek tiny (ya NULL) block return kar sakta hai, lekin program sochta hai ki uske paas elements hain aur block ke bahut door likhta hai — ek classic heap buffer overflow, code execution ke liye exploitable. Yahi wajah hai ki multiplication ko allocate karne se pehle validate karna zaroori hai.

Exercise 5.2

Parent note stress karta hai ki "calloc overflow-safe hai" C standard se guaranteed nahi hai. (a) Standard ka stance kya hai? (b) count * size ke liye ek portable manual overflow check likho jo implementation se independent ho.

Recall Solution 5.2

(a) Kya calloc(count, size) count * size ka overflow detect karta hai yeh implementation-defined hai. Glibc, musl, aur zyaadatar quality libcs overflow par NULL return karte hain, lekin ek conforming minimal implementation ko nahi karna padta — toh iske par rely karna non-portable hai.

(b) Portable check — verify karo ki division multiplication ko invert karta hai:

void *safe_alloc(size_t count, size_t size) {
    if (size != 0 && count > SIZE_MAX / size)
        return NULL;                 // overflow ho jaata -> refuse karo
    return malloc(count * size);     // ab provably safe hai
}

Test count > SIZE_MAX / size exactly tab true hota hai jab count * size SIZE_MAX exceed karta. Hum pehle size != 0 guard karte hain kyunki zero se divide karna undefined hai; agar size == 0 ho toh overflow kuch bhi nahi kar sakta.

Mastery drill:

Kya calloc ka overflow protection C standard se guaranteed hai?
Nahi — yeh implementation-defined hai; ise ek practical bonus ki tarah treat karo, portable guarantee ki tarah nahi.
Kaunsa ek arithmetic test count * size overflow portably detect karta hai?
size != 0 && count > SIZE_MAX / size.

Wrap-up recall

Recall Har function ke liye ek sentence

malloc = fast dirty bytes; calloc = zeroed bytes + count × size split; realloc = data preserve karte hue grow/shrink karo (ek tmp use karo!); free = exactly ek baar wapas do, phir = NULL.

Recall Teen habits jo 90% heap bugs prevent karte hain
  1. Return ko NULL ke liye check karo. 2. Exactly ek baar free karo, phir pointer ko NULL set karo. 3. realloc ke liye sizeof(*p) aur ek tmp variable use karo.

Yeh bhi dekho: Arrays vs Pointers, The Stack and the Heap.