thecodex.expert · The Codex Family of Knowledge
C

Dynamic Memory Allocation

Every malloc needs exactly one matching free — miss it, and that memory is gone for the rest of the program’s life; free it twice, and the behavior is undefined and often crashes.

C17 / C23 No garbage collector Last verified:
Canonical Definition

malloc(size) requests a block of size bytes from the heap, returning a pointer to it (or NULL if the request fails); free(ptr) releases that block back to the system. calloc additionally zero-initializes the memory it allocates; realloc resizes an existing allocation, possibly moving it to a new address. Unlike languages with automatic memory management, C never reclaims heap memory on its own — every successful malloc/calloc/realloc must eventually be matched by exactly one free, or that memory leaks for the remainder of the program's run.

malloc and free: the basic pair

Always check malloc's return value against NULL before using it — a failed allocation (the system is out of memory) returns NULL, and dereferencing that would crash the program immediately.

Cbasic_malloc.c
int *numbers = malloc(5 * sizeof(int));   // room for 5 ints
if (numbers == NULL) {
    fprintf(stderr, "allocation failed\n");
    return 1;
}

for (int i = 0; i < 5; i++) {
    numbers[i] = i * i;
}

free(numbers);   // release the memory — required, nothing does this automatically
numbers = NULL;    // good practice: prevents accidentally using the now-invalid pointer

Two real, common bugs: leaks and use-after-free

Neither of these is caught by the compiler — a leak just silently consumes more and more memory over a program's life; a use-after-free can appear to work by coincidence and then crash unpredictably later, since the freed memory might still contain its old data until something else overwrites it.

Cmemory_bugs.c
// LEAK: allocated memory never freed
void leaky(void) {
    int *data = malloc(100 * sizeof(int));
    // ... use data ...
    // missing free(data) — this memory is gone until the program exits
}

// USE-AFTER-FREE: accessing memory after it's been released
int *p = malloc(sizeof(int));
*p = 42;
free(p);
printf("%d\n", *p);   // UNDEFINED BEHAVIOR — p no longer owns this memory

realloc: resizing an existing allocation

realloc may need to move the block to a different address if it can't grow in place — always capture its return value in a new variable rather than overwriting the original pointer directly, since realloc returns NULL on failure without freeing the original block, and overwriting the only reference to it would leak that memory.

Crealloc_example.c
int *arr = malloc(5 * sizeof(int));

int *bigger = realloc(arr, 10 * sizeof(int));   // grow to 10 ints
if (bigger == NULL) {
    // realloc failed — arr is STILL VALID and must still be freed
    free(arr);
    return 1;
}
arr = bigger;   // safe to reassign now that we know it succeeded

Sources

1
ISO/IEC 9899 (C Standard), "Memory management functions," cppreference.com/w/c/memory.