Pointers & Memory · intermediate · ~12 min

malloc and the heap

Allocate memory whose size is only known at runtime.

Overview

malloc(n) asks the operating system for n bytes of heap memory and returns a pointer to it (or NULL if the allocation failed). The memory's contents are uninitialised. When you're done, you must call free(p) exactly once. Forget, and you have a memory leak; do it twice, and you have a double-free crash.

Why it matters

Stack variables are fixed in size at compile time. The moment your program needs a variable-sized buffer — a string of user-typed length, an array of N items where N is read at runtime, a tree of unknown shape — you need heap allocation. Every dynamic data structure in C builds on malloc and free.

Core concepts

Heap vs stack. The stack is per-function, automatic, fast, fixed-size; the heap is process-wide, manual, slower, grown by syscalls. Ownership. Every malloc must have exactly one matching free. Document who owns each pointer — comments like // caller frees save lives. Alignment. malloc returns a pointer suitable for any type's alignment; you can cast it to whatever you need.

Syntax notes

int *a = malloc(100 * sizeof(int));   // 100 ints, uninitialised
if (!a) { /* allocation failed */ }    // always check
a[0] = 42;
free(a);                                // exactly once
a = NULL;                               // belt-and-braces — prevents UAF later

Lesson

void *malloc(size_t n) returns a pointer to n bytes of uninitialized memory on the heap, or NULL on failure. You must free it later or you leak.

Always check the return value, multiply correctly (malloc(n * sizeof(T))), and remember that the contents are garbage until you write to them.

Code examples

int *a = malloc(10 * sizeof(int));
if (!a) return -1;
for (int i = 0; i < 10; i++) a[i] = i;
free(a);

Common mistakes

  • Forgetting to multiply by sizeof(T) — allocates way too little.
  • Casting the result in C: (int*)malloc(...) is unnecessary in C and can mask #include mistakes.

Debugging tips

Run under valgrind ./a.out to find leaks (memory you never freed) and invalid reads/writes. Compile with -fsanitize=address for a faster but slightly less detailed leak detector. If your program seems to slow down over time, you probably have a leak — every malloc that never gets freed permanently grows your process's footprint.

Memory safety

(1) Always check malloc's return for NULL — out-of-memory is rare but real. (2) Track ownership: who must free this pointer? Comment it. (3) Free exactly once: a double-free corrupts the allocator's bookkeeping and crashes (or worse, silently overlaps two allocations). (4) Treat the buffer as uninitialised — use calloc(n, sz) if you want zeroed memory.

Real-world uses

Dynamic arrays, linked lists, trees, strings of unknown length, parsed JSON / XML trees, hash tables, every game engine's per-frame object pool.

Practice tasks

  1. Allocate an int array of size N (read from stdin), fill it, sum it, and free it. 2. Allocate a string with malloc(strlen(src)+1) and strcpy into it. 3. Make a function that returns a malloc'd buffer the caller must free; document ownership in a comment.

Summary

malloc gives you variable-size memory; free returns it. Every malloc has exactly one free. Check the return value, set freed pointers to NULL, and document ownership. Master these habits and the heap is a tool, not a trap.

Practice with these exercises