Dynamic Memory — The Heap

malloc, calloc, realloc, free — heap-এর সঠিক ব্যবহার

~40 min Advanced 20 practice problems Live code

1. Stack vs Heap

StackHeap
LifetimeFunction call-এর সময়free না করা পর্যন্ত
Sizeছোট (কয়েক MB)অনেক বড় (GB)
Speedদ্রুততুলনামূলক ধীর
Managed byCompiler নিজেআপনি
AllocationAutomaticExplicit (malloc)
ছোট ও ক্ষণস্থায়ী data stack-এ, বড় বা দীর্ঘস্থায়ী data heap-এ। Heap আপনার নিজের দায়িত্ব — সঠিকভাবে allocate করুন, কাজ শেষ হলে অবশ্যই free করুন।

2. malloc, calloc, realloc, free

alloc_demo.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n = 5;

    int *a = malloc(n * sizeof *a);    // uninitialized
    int *b = calloc(n, sizeof *b);       // zero-filled
    if (!a || !b) { perror("alloc"); return 1; }

    for (int i = 0; i < n; i++) a[i] = i * 10;

    printf("malloc'd a : ");
    for (int i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\ncalloc'd b : ");
    for (int i = 0; i < n; i++) printf("%d ", b[i]);
    putchar('\n');

    // grow a to double size
    int *a2 = realloc(a, (n * 2) * sizeof *a2);
    if (!a2) { free(a); free(b); return 1; }
    a = a2;
    for (int i = n; i < n * 2; i++) a[i] = i * 10;

    printf("after realloc: ");
    for (int i = 0; i < n * 2; i++) printf("%d ", a[i]);
    putchar('\n');

    free(a); a = NULL;
    free(b); b = NULL;
    return 0;
}
Idiom T *p = malloc(n * sizeof *p); — sizeof *p লেখায় type পরিবর্তন করলে বাকি কোড একই থাকে। এটি বেশি নিরাপদ এবং বজায় রাখা সহজ।

3. Always Check the Return Value

int *a = malloc(n * sizeof *a);
if (!a) {
    fprintf(stderr, "out of memory\n");
    exit(1);
}
System-এর memory শেষ হলে malloc NULL ফেরত দেয়। NULL check না করলে পরবর্তীতে dereference করলে crash হবে।

4. The Four Deadly Bugs

Memory Leak
free করতে ভুলে যাওয়া — memory ক্রমাগত বাড়ে, কখনো কখনো প্রোগ্রাম ধীর হয়ে যায়।
Double Free
একই pointer দু'বার free করা — heap corruption, security vulnerability।
Use After Free
free-এর পরে dereference করা — কখনো "কাজ করে" মনে হয়, কিন্তু পরে crash বা wrong output।
Buffer Overrun
বরাদ্দকৃত size-এর বাইরে লেখা — পাশের data বা heap metadata corrupt হয়।

Tools: valgrind ./prog, gcc -fsanitize=address — এই দুটি tool বাগ খুঁজে বের করার জন্য অপরিহার্য।

5. Ownership — Who Frees What?

প্রতিটি allocation-এর একটি পরিষ্কার owner থাকতে হবে, যার দায়িত্ব free করা। Code-এ document করুন:

/* Returns a newly allocated array. Caller owns it and must free(). */
int *make_array(int n);

6. A Dynamic Array (Vec) Pattern

vec.c
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int *data;
    size_t len, cap;
} Vec;

void vec_push(Vec *v, int x) {
    if (v->len == v->cap) {
        v->cap = v->cap ? v->cap * 2 : 4;
        v->data = realloc(v->data, v->cap * sizeof *v->data);
    }
    v->data[v->len++] = x;
}

void vec_free(Vec *v) { free(v->data); v->data = NULL; v->len = v->cap = 0; }

int main(void) {
    Vec v = {0};
    for (int i = 1; i <= 10; i++) vec_push(&v, i * i);
    printf("len=%zu, cap=%zu\n", v.len, v.cap);
    for (size_t i = 0; i < v.len; i++) printf("%d ", v.data[i]);
    putchar('\n');
    vec_free(&v);
    return 0;
}
Capacity দ্বিগুণ করে বাড়ানোর এই pattern amortized O(1) push দেয়। আধুনিক সব dynamic array (C++ std::vector, Python list, Go slice) এই pattern ব্যবহার করে।

7. Practice Problems

  1. Allocate an array of N ints, fill with 1..N, print, then free.
    N-সাইজের int array allocate করে 1..N বসিয়ে print করুন, শেষে free।
    ✨ Show Answer
    fill_n.c — stdin: 7
    #include <stdio.h>
    #include <stdlib.h>
    int main(void) {
        int n; scanf("%d", &n);
        int *a = malloc(n * sizeof *a);
        if (!a) return 1;
        for (int i = 0; i < n; i++) a[i] = i + 1;
        for (int i = 0; i < n; i++) printf("%d ", a[i]);
        putchar('\n');
        free(a);
        return 0;
    }
  2. Read N from user and allocate exactly N doubles.
    User থেকে N নিয়ে ঠিক N-সাইজের double array allocate করুন।
    ✨ Show Answer
    doubles.c — stdin: 3 / 1.1 2.2 3.3
    #include <stdio.h>
    #include <stdlib.h>
    int main(void) {
        int n; scanf("%d", &n);
        double *a = malloc(n * sizeof *a);
        if (!a) return 1;
        for (int i = 0; i < n; i++) scanf("%lf", &a[i]);
        double s = 0;
        for (int i = 0; i < n; i++) s += a[i];
        printf("sum = %.4f\n", s);
        free(a);
        return 0;
    }
  3. Build the Vec above — add vec_pop.
    উপরের Vec-এ vec_pop যোগ করুন।
    ✨ Show Answer
    vec_pop.c
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct { int *data; size_t len, cap; } Vec;
    
    void vec_push(Vec *v, int x) {
        if (v->len == v->cap) {
            v->cap = v->cap ? v->cap * 2 : 4;
            v->data = realloc(v->data, v->cap * sizeof *v->data);
        }
        v->data[v->len++] = x;
    }
    
    int vec_pop(Vec *v) {          // returns 1 if popped, 0 if empty
        if (v->len == 0) return 0;
        v->len--;
        return 1;
    }
    
    void vec_free(Vec *v) { free(v->data); *v = (Vec){0}; }
    
    int main(void) {
        Vec v = {0};
        for (int i = 1; i <= 5; i++) vec_push(&v, i);
        vec_pop(&v);
        vec_pop(&v);
        printf("after 2 pops, len=%zu\n", v.len);
        vec_free(&v);
        return 0;
    }
  4. Read words of unknown length using malloc + realloc.
    Malloc + realloc দিয়ে অজানা দৈর্ঘ্যের word পড়ুন।
    ✨ Show Answer
    grow_read.c — stdin: ABCLTECH
    #include <stdio.h>
    #include <stdlib.h>
    int main(void) {
        size_t cap = 4, len = 0;
        char *buf = malloc(cap);
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            if (len + 1 >= cap) {
                cap *= 2;
                buf = realloc(buf, cap);
            }
            buf[len++] = c;
        }
        buf[len] = '\0';
        printf("read %zu chars: %s\n", len, buf);
        free(buf);
        return 0;
    }
  5. Implement char *my_strdup(const char *s).
    my_strdup ফাংশন লিখুন।
    ✨ Show Answer
    strdup.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char *my_strdup(const char *s) {
        size_t n = strlen(s) + 1;
        char *p = malloc(n);
        if (p) memcpy(p, s, n);
        return p;
    }
    
    int main(void) {
        char *copy = my_strdup("Hello, Bangladesh!");
        puts(copy);
        free(copy);
        return 0;
    }
  6. Allocate a 2D grid using int **.
    int ** দিয়ে 2D grid allocate করুন।
    ✨ Show Answer
    grid.c
    #include <stdio.h>
    #include <stdlib.h>
    int main(void) {
        int R = 3, C = 4;
        int **g = malloc(R * sizeof *g);
        for (int i = 0; i < R; i++) g[i] = calloc(C, sizeof **g);
    
        for (int i = 0; i < R; i++)
            for (int j = 0; j < C; j++) g[i][j] = i * C + j;
    
        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) printf("%3d ", g[i][j]);
            putchar('\n');
        }
    
        // Free in reverse order
        for (int i = 0; i < R; i++) free(g[i]);
        free(g);
        return 0;
    }
  7. Show how to free a 2D grid correctly (rows first, then the row-pointer array).
    2D grid সঠিকভাবে free করার পদ্ধতি দেখান (আগে row, পরে outer array)।
    ✨ Show Answer

    উপরের প্রশ্নের grid.c-ই বিস্তারিত উত্তর। নিয়ম: allocation যেভাবে তৈরি হয়েছে, free তার উল্টো ক্রমে করতে হবে।

  8. Demonstrate a memory leak, then fix it.
    একটি memory leak দেখান এবং ঠিক করুন।
    ✨ Show Answer
    // ❌ leak — never freed
    void leaky(void) {
        int *a = malloc(1000 * sizeof *a);
        a[0] = 42;
    }    // a lost when function returns → leak
    
    // ✅ fixed
    void clean(void) {
        int *a = malloc(1000 * sizeof *a);
        a[0] = 42;
        free(a);
    }

    Leak ধরার জন্য Linux-এ valgrind ./prog, macOS/Linux-এ gcc -fsanitize=address ব্যবহার করুন।

  9. Demonstrate a double-free bug, then fix.
    Double-free বাগ দেখান এবং ঠিক করুন।
    ✨ Show Answer
    // ❌ double free
    int *p = malloc(4);
    free(p);
    free(p);    // UB — heap corruption
    
    // ✅ null the pointer after free
    free(p); p = NULL;
    free(p);    // safe — free(NULL) is a no-op
  10. Demonstrate a use-after-free bug, then fix.
    Use-after-free বাগ দেখান এবং ঠিক করুন।
    ✨ Show Answer
    // ❌ use after free
    int *p = malloc(sizeof *p);
    *p = 42;
    free(p);
    printf("%d\n", *p);    // UB
    
    // ✅ null the pointer after free so a future deref crashes loudly
    free(p); p = NULL;
  11. Implement int *grow(int *a, int old_n, int new_n) using realloc.
    realloc দিয়ে grow ফাংশন লিখুন।
    ✨ Show Answer
    grow.c
    #include <stdio.h>
    #include <stdlib.h>
    
    int *grow(int *a, int old_n, int new_n) {
        int *p = realloc(a, new_n * sizeof *p);
        if (!p) return NULL;            // original a still valid
        for (int i = old_n; i < new_n; i++) p[i] = 0;
        return p;
    }
    
    int main(void) {
        int *a = malloc(4 * sizeof *a);
        for (int i = 0; i < 4; i++) a[i] = i;
        a = grow(a, 4, 8);
        for (int i = 0; i < 8; i++) printf("%d ", a[i]);
        putchar('\n');
        free(a);
        return 0;
    }
  12. Why can realloc return a different pointer?
    realloc কখনো ভিন্ন pointer কেন ফেরত দেয়?
    ✨ Show Answer

    Current block-এর পরে যদি যথেষ্ট জায়গা না থাকে, realloc heap-এ অন্য জায়গায় নতুন block নেয়, পুরাতন content কপি করে এবং পুরাতন block free করে দেয়। তাই ফেরত আসা pointer আগেরটি থেকে আলাদা হতে পারে। সবসময় এভাবে লিখুন: t = realloc(a, n); if (!t) handle_err(); else a = t; — সরাসরি a = realloc(a, n); লিখলে realloc ব্যর্থ হলে মূল a-ও হারিয়ে যায়।

  13. What happens if you free(NULL)?
    free(NULL) করলে কী হয়?
    ✨ Show Answer

    C standard অনুযায়ী free(NULL) একটি valid no-op — কিছুই ঘটে না, crash হয় না। তাই cleanup কোডে extra NULL-check লিখতে হয় না।

  14. Build a dynamic string builder that grows as you append chars.
    Append-এর সাথে বাড়ে এমন একটি dynamic string builder বানান।
    ✨ Show Answer
    strbuilder.c
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct { char *buf; size_t len, cap; } SB;
    
    void sb_push(SB *s, char c) {
        if (s->len + 1 >= s->cap) {
            s->cap = s->cap ? s->cap * 2 : 16;
            s->buf = realloc(s->buf, s->cap);
        }
        s->buf[s->len++] = c;
        s->buf[s->len]    = '\0';
    }
    
    int main(void) {
        SB s = {0};
        const char *msg = "Hello, Bangladesh!";
        for (const char *p = msg; *p; p++) sb_push(&s, *p);
        puts(s.buf);
        free(s.buf);
        return 0;
    }
  15. Show that malloc(0) is implementation-defined.
    malloc(0) আচরণ implementation-নির্ভর — পরীক্ষা করুন।
    ✨ Show Answer
    zero.c
    #include <stdio.h>
    #include <stdlib.h>
    int main(void) {
        void *p = malloc(0);
        printf("malloc(0) returned %s\n", p ? "a non-null pointer" : "NULL");
        free(p);
        return 0;
    }

    Standard অনুযায়ী দুটি আচরণ বৈধ: NULL দিতে পারে, অথবা একটি unique pointer দিতে পারে যা free-যোগ্য। কোনো ক্ষেত্রেই সেই pointer dereference করা উচিত নয়।

  16. Read a text file fully into a single malloc'd buffer (demonstrated without a real file).
    পুরো text ফাইলকে একটি buffer-এ পড়ার ধরন দেখান।
    ✨ Show Answer
    #include <stdio.h>
    #include <stdlib.h>
    char *slurp(const char *path) {
        FILE *f = fopen(path, "rb");
        if (!f) return NULL;
        fseek(f, 0, SEEK_END);
        long n = ftell(f);
        rewind(f);
        char *buf = malloc(n + 1);
        fread(buf, 1, n, f);
        buf[n] = '\0';
        fclose(f);
        return buf;
    }

    Online runner-এ সাধারণত filesystem-এ file তৈরি করা যায় না, তাই এটি pattern হিসেবে দেখানো হলো — নিজের মেশিনে পরীক্ষা করুন।

  17. Write a macro SAFE_FREE(p) that frees and nulls the pointer.
    SAFE_FREE macro লিখুন যা free করে ও NULL বসিয়ে দেয়।
    ✨ Show Answer

    L14-এর safe_free.c-ই এর সম্পূর্ণ উত্তর।

  18. Allocate and free a large block repeatedly; observe memory usage.
    বড় block বারবার allocate/free করে memory usage পর্যবেক্ষণ করুন।
    ✨ Show Answer
    churn.c
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
        int rounds = 1000;
        for (int i = 0; i < rounds; i++) {
            int *p = malloc(100000 * sizeof *p);
            if (!p) { puts("oom"); return 1; }
            p[0] = i;
            free(p);
        }
        puts("done — no leak");
        return 0;
    }

    Local machine-এ top / Task Manager চালিয়ে দেখুন — memory usage স্থিতিশীল থাকে।

  19. Explain fragmentation in one paragraph.
    Memory fragmentation এক অনুচ্ছেদে ব্যাখ্যা করুন।
    ✨ Show Answer

    দীর্ঘ সময় ধরে ভিন্ন আকারের অনেক allocation/free করার পর heap-এ অনেক ছোট ছোট খালি জায়গা তৈরি হয়, যার মধ্যে মোট জায়গা যথেষ্ট হলেও পরপর একটি বড় block-এর জন্য জায়গা মেলে না — এটিই fragmentation। এটি malloc-কে fail করাতে পারে, বা OS থেকে আরও memory চাইতে বাধ্য করতে পারে। সমাধান: একই আকারের জিনিস জন্য memory pool, ভালো allocator (jemalloc), আবার large-then-small বা long-lived-then-short-lived আলাদা করে রাখা।

  20. Compare allocating once big vs many small — what's the cost difference?
    একবারে বড় vs অনেকবার ছোট allocation — পার্থক্য কী?
    ✨ Show Answer

    একবারে বড় allocation কম syscall/overhead, কম fragmentation, ভালো cache locality দেয় — তাই দ্রুত। অনেক ছোট allocation-এ প্রতিটির জন্য internal bookkeeping (header) থাকে, fragment বাড়ে, cache miss বেশি হয়। Competitive programming বা high-performance কোডে সম্ভব হলে একবারই বড় buffer নিন, তারপর সেখান থেকে বিতরণ করুন (arena/pool allocator)।

Glossary (শব্দকোষ)

TermMeaningবাংলায়
HeapThe memory region for dynamic, manually managed allocations.Dynamic, hand-managed memory region।
StackMemory for local variables — automatically managed.Local variable-এর জন্য স্বয়ংক্রিয় মেমরি।
mallocAllocates uninitialized bytes on the heap.Heap-এ uninitialized memory বরাদ্দ।
callocAllocates zero-initialized bytes.Zero-initialized memory বরাদ্দ।
reallocResizes an existing heap allocation.আগের allocation-এর size পরিবর্তন।
freeReleases heap memory back to the allocator.Heap memory ফেরত দেওয়া।
Memory LeakHeap memory that is never freed.Free না-করা heap memory।
Dangling PointerA pointer that still holds an address after free.free-এর পরও পুরনো address ধরে থাকা pointer।
Double FreeCalling free twice on the same pointer — undefined behavior.একই pointer-কে দুবার free।
Use-after-freeReading/writing memory after it was freed.Free হওয়ার পর সেই memory ব্যবহার।
Heap FragmentationFree space scattered into unusable small chunks.Free space ছোট ছোট খণ্ডে ছড়িয়ে পড়া।
OwnershipThe convention deciding who is responsible for freeing memory.কে memory free করবে তার নিয়ম।
Valgrind / ASanTools that detect memory bugs at runtime.Runtime-এ memory bug ধরার টুল।

Summary — Module 17

Heap is where long-lived or unknown-size data lives. Pair every malloc with a free. Check for NULL. Clear freed pointers. Use valgrind / AddressSanitizer. Establish ownership.

Next Module → Structures, unions & bit fields।