C Mastery / The C Memory Model: Stack, Heap, Static, Thread-Local
Part 4 — Memory Management

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

Core Concept

The C standard defines four storage durations; implementations map them to regions of the process's address space:

Storage durationTypical regionLifetime
Staticdata / bss / rodatawhole program
Automaticstackblock entry → block exit
Threadthread-local storage (TLS)whole thread
Allocatedheapmalloc → free

The regions

globals). Read-only; writing is UB.

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

Undefined Behavior

Portability

addresses, sizes, and growth direction are implementation-defined.

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

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.

References

environments).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

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