Code of the Day
IntermediateMemory management

Stack vs. heap

Understand the two memory regions every C program uses — the stack for local variables and the heap for dynamic allocation — and when to use each.

CIntermediate11 min read
By the end of this lesson you will be able to:
  • Describe the stack and explain how function calls use it
  • Describe the heap and explain why it exists
  • Explain the lifetime differences between stack and heap memory
  • Choose the appropriate region for a given use case

Every C program has access to two major memory regions: the stack and the heap. Understanding the difference is fundamental not just for C but for every language you will ever use — Python, Java, Go, and Rust all build their memory models on top of these same underlying mechanisms.

The stack

The stack is a region of memory managed automatically by the CPU and your program's runtime. When a function is called, the CPU pushes a stack frame onto the stack: space for the function's local variables, its parameters, and a return address. When the function returns, the frame is popped and that memory is immediately available for reuse.

#include <stdio.h>

void inner(void) {
    int x = 10; /* lives on the stack */
    printf("inner: x at %p\n", (void *)&x);
} /* x is gone here -- stack frame popped */

void outer(void) {
    int y = 20; /* lives on the stack */
    inner();    /* pushes another frame */
    printf("outer: y at %p\n", (void *)&y);
} /* y is gone here */

int main(void) {
    outer();
    return 0;
}

Stack characteristics:

  • Automatic lifetime: allocated when a variable's scope is entered, freed when it is exited.
  • Fixed size: typically 1–8 MB. Deep recursion or large local arrays can overflow it (stack overflow).
  • Fast: a single CPU instruction adjusts the stack pointer to allocate space.
  • No fragmentation: allocations and frees happen in LIFO order.

The heap

The heap is a region of memory you manage manually with malloc and free. It can be much larger than the stack (limited by available RAM), and its lifetime is explicit: memory lives from malloc until you call free.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n = 1000000;
    int *big_array = malloc(n * sizeof(int)); /* 4 MB on the heap */

    if (big_array == NULL) {
        fprintf(stderr, "malloc failed\n");
        return 1;
    }

    for (int i = 0; i < n; i++) {
        big_array[i] = i;
    }

    printf("First: %d, Last: %d\n", big_array[0], big_array[n - 1]);

    free(big_array); /* return memory to the heap allocator */
    return 0;
}

Heap characteristics:

  • Manual lifetime: you control when memory is allocated and freed.
  • Large: limited by available virtual memory (gigabytes on modern systems).
  • Slower: the allocator must find a suitable free block, which takes time.
  • Fragmentation: after many allocations and frees, the heap may have many small free blocks but no single large contiguous block.

Why the heap exists

Two main reasons:

Size: A 10 MB array on the stack would overflow it. The heap supports large, long-lived data.

Dynamic size: You often do not know at compile time how large an array needs to be. The user might type 5 items or 5,000. The heap lets you allocate exactly as much as needed at runtime:

int n;
scanf("%d", &n); /* read count at runtime */
int *data = malloc(n * sizeof(int)); /* allocate exactly n ints */

When to use each

Use the stack when:

  • The data's lifetime matches the function's lifetime.
  • The size is known at compile time and is small (a few kilobytes at most).
  • You want automatic cleanup (no free needed).

Use the heap when:

  • The data must outlive the function that creates it.
  • The size is unknown until runtime.
  • The data is too large for the stack.
  • You need to resize the allocation (with realloc).

The stack and heap in memory layout

High addresses
┌─────────────────┐
│   Stack         │ ← grows downward
│   (local vars,  │
│    frames)      │
├─────────────────┤
│   (free space)  │
├─────────────────┤
│   Heap          │ ← grows upward
│   (malloc data) │
├─────────────────┤
│   BSS segment   │ (uninitialised globals)
├─────────────────┤
│   Data segment  │ (initialised globals)
├─────────────────┤
│   Text segment  │ (program code)
└─────────────────┘
Low addresses

The exact layout varies by OS and architecture, but the conceptual picture is consistent.

Stack and heap are OS concepts, not C concepts. The C standard talks about "automatic storage" and "dynamic storage" without specifying they are implemented as a stack and heap. In practice, every mainstream C implementation on every mainstream OS uses exactly this model, so knowing the underlying implementation is valuable.

Where to go next

Next: malloc, calloc, realloc, and free — the four functions that manage heap memory, their exact semantics, and how to use them safely.

Finished reading? Mark it complete to track your progress.

On this page