C CLP study materials
C CLP study resources

C CLP: Dynamic Memory Management

C CLP · Work through your exam, one topic at a time.

All guide sections

Dynamic Memory Management

This domain focuses on the contract of each allocation function, the four ways heap code goes wrong (leak, double free, use-after-free, out-of-bounds access), and how to handle allocation failure correctly.

The allocator interface

  • void *malloc(size_t n) returns uninitialized memory, suitably aligned for any type, or NULL.
  • void *calloc(size_t n, size_t size) zeroes the block and performs the multiplication for you.
  • void *realloc(void *p, size_t n) may move the block. On success the old pointer is invalid; on failure it returns NULL and leaves p untouched. realloc(p, 0) is implementation-defined (it may free or return a minimal block).
  • void free(void *p) accepts NULL and does nothing; any other pointer must have come from an allocation function. free cannot set the caller's pointer to NULL.

Correct patterns

  • Check every allocation for NULL, and include <stdlib.h> so the prototype is visible - in C99 and later, a cast cannot rescue a missing declaration.
  • Write p = malloc(n * sizeof *p) to keep the element type in one place, and guard the multiplication against overflow (or use calloc).
  • Use a temporary for realloc, and release resources on every error path.

Worked example

int *grow(int *a, size_t n) {
    int *tmp = realloc(a, n * sizeof *a);
    if (tmp == NULL) {
        free(a);            /* only if ownership passes to this function */
        return NULL;
    }
    return tmp;
}

Assigning realloc(...) straight back to a would leak the original block when realloc returns NULL: a would be overwritten with NULL and the old pointer lost. Here the failure path is explicit and the caller can decide what to do. After a successful realloc, the old pointer must never be used again.

Traps

  • free(p); free(p); and free(p); p->x = 1; are undefined; set the pointer to NULL after freeing if it may be reused.
  • Freeing an address the allocator never returned (a local variable, or p + 1).
  • Writing sizeof(ptr) instead of sizeof *ptr in the size expression.
  • Assuming malloc or the new part of a realloc block is zeroed; only calloc guarantees zeros.
  • Leaking the block when an early return skips the free.
  • Depending on strdup, which is POSIX rather than ISO C.