C Mastery / malloc, calloc, realloc, free
Part 4 — Memory Management

malloc, calloc, realloc, free

This chapter covers the four dynamic-allocation functions in depth: their contracts, failure modes, and the undefined behavior they can produce if misused.

Why This Matters

Dynamic allocation is how C programs create objects whose size or count is unknown at compile time. Nearly every real data structure uses it, and nearly every memory-safety bug traces back to a violation of these functions' contracts.

Prerequisites

Core Concept

void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void  free(void *ptr);

smaller of old/new sizes are preserved. It may move the block.

All return NULL on failure (except free). The returned pointer is suitably aligned for any standard type (but not necessarily over-aligned types — see c.mem.alignment).

Syntax

int *p = malloc(sizeof *p);
if (p == NULL) { /* handle */ }

int *arr = calloc(n, sizeof *arr);
void *q = realloc(p, new_size);
free(p);

Examples

Allocation and ownership

#include <stdlib.h>

int main(void)
{
    int *p = malloc(sizeof *p);
    if (p == NULL) return 1;
    *p = 42;
    free(p);
    return 0;
}

calloc zeroes

#include <stdlib.h>

int main(void)
{
    int *a = calloc(10, sizeof *a);  /* all elements 0 */
    if (a == NULL) return 1;
    /* ... */
    free(a);
    return 0;
}

realloc without leaking on failure

#include <stdlib.h>

int main(void)
{
    int *p = malloc(4 * sizeof *p);
    if (p == NULL) return 1;

    int *q = realloc(p, 8 * sizeof *p);
    if (q == NULL) {
        /* p is still valid and must still be freed */
        free(p);
        return 1;
    }
    p = q;   /* now safe to reassign */
    free(p);
    return 0;
}

The wrong pattern is p = realloc(p, n) directly — if it fails, p is overwritten with NULL and the original block leaks.

How It Works

The allocator maintains free lists and metadata (block size, flags) around the returned pointer. malloc finds a free block of sufficient size (splitting large blocks), free returns a block to the free list and may coalesce with neighbors, and realloc grows in place if possible or allocates a new block and copies. Full internals are in c.memory.5.

Variations

Zero-size allocations

malloc(0) is implementation-defined: it returns either NULL or a unique pointer that may be passed to free. Avoid relying on it.

Overflow in calloc size

calloc(nmemb, size) checks for multiplication overflow; malloc(n * size) does not (the multiplication can overflow before malloc is called). Prefer calloc for counted allocations.

Common Mistakes

Undefined Behavior

VERIFIED

VERIFIED

Portability

Under the Hood

On Linux, malloc is typically implemented over brk/mmap: small allocations come from the heap (brk), large ones from mmaped regions. The allocator adds a small header before the returned pointer to track size and status.

Practical Usage

Exercises

1. Write a dynamic array that grows with realloc, using the safe temporary pattern. 2. Demonstrate calloc zero-initialization vs. malloc indeterminate contents. 3. Deliberately double-free a pointer and observe ASan's diagnostic. 4. Explain why malloc(n * sizeof(int)) can overflow but calloc(n, sizeof(int)) checks.

Deep Challenge

Implement a small realloc-safe dynamic buffer API with push that grows by a factor (e.g., 2x), handles allocation failure without leaking, and exposes capacity/size. Explain each ownership and overflow decision.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.mem.malloc07