Dynamic Memory — The Heap
malloc, calloc, realloc, free — heap-এর সঠিক ব্যবহার
1. Stack vs Heap
| Stack | Heap | |
|---|---|---|
| Lifetime | Function call-এর সময় | free না করা পর্যন্ত |
| Size | ছোট (কয়েক MB) | অনেক বড় (GB) |
| Speed | দ্রুত | তুলনামূলক ধীর |
| Managed by | Compiler নিজে | আপনি |
| Allocation | Automatic | Explicit (malloc) |
free করুন।2. malloc, calloc, realloc, free
#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;
}
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);
}
malloc NULL ফেরত দেয়। NULL check না করলে পরবর্তীতে dereference করলে crash হবে।4. The Four Deadly Bugs
free করতে ভুলে যাওয়া — memory ক্রমাগত বাড়ে, কখনো কখনো প্রোগ্রাম ধীর হয়ে যায়।একই pointer দু'বার
free করা — heap corruption, security vulnerability।free-এর পরে dereference করা — কখনো "কাজ করে" মনে হয়, কিন্তু পরে crash বা wrong output।বরাদ্দকৃত 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
#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;
}
push দেয়। আধুনিক সব dynamic array (C++ std::vector, Python list, Go slice) এই pattern ব্যবহার করে।7. Practice Problems
- 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; } - 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; } - 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; } - 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; } - 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; } - 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; } - 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 তার উল্টো ক্রমে করতে হবে। - 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ব্যবহার করুন। - 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 - 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; - 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; } - Why can
reallocreturn a different pointer?reallocকখনো ভিন্ন pointer কেন ফেরত দেয়?✨ Show Answer
Current block-এর পরে যদি যথেষ্ট জায়গা না থাকে,
reallocheap-এ অন্য জায়গায় নতুন block নেয়, পুরাতন content কপি করে এবং পুরাতন block free করে দেয়। তাই ফেরত আসা pointer আগেরটি থেকে আলাদা হতে পারে। সবসময় এভাবে লিখুন:t = realloc(a, n); if (!t) handle_err(); else a = t;— সরাসরিa = realloc(a, n);লিখলেreallocব্যর্থ হলে মূলa-ও হারিয়ে যায়। - What happens if you
free(NULL)?free(NULL)করলে কী হয়?✨ Show Answer
C standard অনুযায়ী
free(NULL)একটি valid no-op — কিছুই ঘটে না, crash হয় না। তাই cleanup কোডে extra NULL-check লিখতে হয় না। - 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; } - 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 করা উচিত নয়। - 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 হিসেবে দেখানো হলো — নিজের মেশিনে পরীক্ষা করুন।
- Write a macro
SAFE_FREE(p)that frees and nulls the pointer.SAFE_FREEmacro লিখুন যা free করে ও NULL বসিয়ে দেয়।✨ Show Answer
L14-এর
safe_free.c-ই এর সম্পূর্ণ উত্তর। - 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 স্থিতিশীল থাকে। - 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 আলাদা করে রাখা। - 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 (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
| Heap | The memory region for dynamic, manually managed allocations. | Dynamic, hand-managed memory region। |
| Stack | Memory for local variables — automatically managed. | Local variable-এর জন্য স্বয়ংক্রিয় মেমরি। |
malloc | Allocates uninitialized bytes on the heap. | Heap-এ uninitialized memory বরাদ্দ। |
calloc | Allocates zero-initialized bytes. | Zero-initialized memory বরাদ্দ। |
realloc | Resizes an existing heap allocation. | আগের allocation-এর size পরিবর্তন। |
free | Releases heap memory back to the allocator. | Heap memory ফেরত দেওয়া। |
| Memory Leak | Heap memory that is never freed. | Free না-করা heap memory। |
| Dangling Pointer | A pointer that still holds an address after free. | free-এর পরও পুরনো address ধরে থাকা pointer। |
| Double Free | Calling free twice on the same pointer — undefined behavior. | একই pointer-কে দুবার free। |
| Use-after-free | Reading/writing memory after it was freed. | Free হওয়ার পর সেই memory ব্যবহার। |
| Heap Fragmentation | Free space scattered into unusable small chunks. | Free space ছোট ছোট খণ্ডে ছড়িয়ে পড়া। |
| Ownership | The convention deciding who is responsible for freeing memory. | কে memory free করবে তার নিয়ম। |
| Valgrind / ASan | Tools 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.