The C Memory Model: Stack, Heap, Static, Thread-Local
This chapter maps each storage duration to a concrete memory region — stack, heap, static storage, and thread-local storage — and explains the lifetime and performance consequences of each.
Why This Matters
Every pointer points *somewhere*. Knowing which region a pointer refers to tells you how long the object lives, whether it can outlive a function, and what its performance characteristics are. This is the mental map for all of memory management.
Prerequisites
c.core.7— scope, storage duration, lifetime.
Core Concept
The C standard defines four storage durations; implementations map them to regions of the process's address space:
| Storage duration | Typical region | Lifetime |
|---|---|---|
| Static | data / bss / rodata | whole program |
| Automatic | stack | block entry → block exit |
| Thread | thread-local storage (TLS) | whole thread |
| Allocated | heap | malloc → free |
The regions
- Text / rodata: machine code and read-only data (string literals, const
globals). Read-only; writing is UB.
- Data: initialized static objects (explicit nonzero or zero).
- BSS: uninitialized static objects, zero-initialized before
main. - Stack: automatic objects and call frames. Grows and shrinks with calls.
- Heap: allocated objects, managed by
malloc/free. - TLS: per-thread static-duration objects.
Examples
Observing storage regions
#include <stdio.h>
#include <stdlib.h>
int global; /* BSS (zero-initialized) */
int initialized = 5; /* data */
static const int K = 7; /* rodata (if not optimized into code) */
int main(void)
{
int local = 1; /* stack */
int *p = malloc(sizeof *p); /* heap */
if (p) { *p = 2; free(p); }
printf("global=%d initialized=%d local=%d\n", global, initialized, local);
return 0;
}
The exact addresses are implementation-defined; the *regions* and their lifetimes are what matter.
How It Works
The linker places static objects into data/bss/rodata sections. At run time, the stack grows with each function call and shrinks on return. The heap is a managed region that malloc carves into blocks. TLS is a per-thread area the runtime sets up when a thread starts.
Variations
Stack direction and growth
On x86 and most ARM, the stack grows *downward* (toward lower addresses). The direction is implementation-specific and must not be relied on in portable code.
Embedded systems
In freestanding environments there may be no heap, a tiny stack, and static storage in flash (c.embedded.1).
Common Mistakes
- Assuming an automatic object survives after its function returns.
- Assuming a pointer to a string literal is writable (it is in rodata).
- Assuming heap memory is zeroed (it is not; use
calloc). - Ignoring that the stack is finite (deep recursion overflows it).
Undefined Behavior
- Writing to a string literal (rodata).
VERIFIED - Using an object outside its lifetime (any region).
VERIFIED - Stack overflow that accesses beyond the stack region.
VERIFIED
Portability
- The existence and rough behavior of these regions are universal, but their
addresses, sizes, and growth direction are implementation-defined.
- TLS is C11 (
_Thread_local).
Under the Hood
The data/bss/rodata/text segments are laid out by the linker (c.build.2). The stack pointer register tracks the top of the stack (c.cpu.sp). The heap is a runtime data structure managed by the allocator (c.memory.5).
Practical Usage
- Use static storage for data that must persist and is cheap to keep.
- Use automatic storage for short-lived, small data.
- Use the heap for data whose size or lifetime is unknown at compile time.
- Use TLS for per-thread state (
c.conc.1).
Exercises
1. Write a program that prints the addresses of a global, a static local, an automatic local, and a malloced block; identify which region each is in. 2. Demonstrate that a string literal cannot be written (observe the crash). 3. Show that a static local keeps its value across calls while an automatic local does not.
Deep Challenge
Explain why returning a pointer to a string literal is safe but returning a pointer to a local array is not, in terms of the memory regions involved. Then show a correct heap-based alternative.
Related Concepts
c.lang.storage-duration— storage durations.c.memory.2— malloc/calloc/realloc/free.c.cpu.2— stack frames.c.build.2— sections.
References
- ISO/IEC 9899:2018 §6.2.4 (storage durations), §5.1.2 (execution
environments).
Verification
- Four storage durations and their region mapping.
VERIFIED - Static storage zero-initialized.
VERIFIED - String literals are not modifiable.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Stack, heap, static, TLS regions
- [ ] text/rodata/data/bss
- [ ] Storage region lifetimes
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.mem.model | 0 | 6 |