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.
- 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
freeneeded).
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 addressesThe 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.
Lab: Implement strlen and strcpy from scratch
Build your own versions of strlen, strcpy, strcmp, and strcat using raw pointer manipulation — cementing everything from the Pointers module.
malloc, calloc, realloc, and free
Allocate and manage heap memory in C using malloc, calloc, realloc, and free — including correct usage patterns and common mistakes.