Skip to content
  • Memory Layout of a C Program · C

Memory Layout of a C Program

LearnViaNotes @learnvianotes· 3 min read

A running C program’s address space is split into segments: code, read-only data, initialised data, zero-initialised data (bss), the heap growing up and the stack growing down.

SegmentHoldsLifetimeNotes
Text (.text)Machine code of functionsProgramRead-only + executable; shared between processes
Read-only data (.rodata)String literals, const globalsProgramWriting crashes
Data (.data)Initialised globals / statics (int g = 5;)ProgramValues stored in the executable file
BSS (.bss)Zero / uninitialised globals / staticsProgramOnly the size is stored; zero-filled at load
Heapmalloc / calloc / reallocUntil freeGrows upward; managed by the allocator
StackLocals, parameters, return addressesUntil the function returnsGrows downward; ~8 MB default on Linux/macOS main thread

high addresses: stack ↓ → (free space) → ↑ heap → .bss → .data → .rodata → low addresses: .text

C — addresses differ per run and platform (no fixed output)
#include <stdio.h>
#include <stdlib.h>

int initialised = 42;          /* .data */
int zeroed;                    /* .bss  */
const char *msg = "literal";   /* pointer in .data, the characters in .rodata */

void where(void) { }           /* .text */

int main(void) {
    int local = 1;                              /* stack */
    static int counter;                         /* .bss (static, zero) */
    int *heap = malloc(sizeof *heap);           /* heap  */
    if (!heap) return 1;
    printf("text   %p\n", (void *)(size_t)where);
    printf("rodata %p\n", (const void *)msg);
    printf("data   %p\n", (void *)&initialised);
    printf("bss    %p %p\n", (void *)&zeroed, (void *)&counter);
    printf("heap   %p\n", (void *)heap);
    printf("stack  %p\n", (void *)&local);
    free(heap);
    return 0;
}

Inspecting the segments

Terminal
$ size ./app                      # text / data / bss sizes (Linux)
   text    data     bss     dec     hex filename
   1845     616       8    2469     9a5 ./app
$ objdump -h ./app                # section headers
$ nm ./app | grep ' [bBdD] '      # symbols in bss (b/B) and data (d/D)
$ ulimit -s                       # stack size limit in KB
8192

Stack vs heap

StackHeap
AllocationAutomatic (move the stack pointer)Explicit malloc / free
SpeedVery fastSlower (allocator bookkeeping, possible system call)
SizeSmall, fixed limitLarge — limited by RAM / address space
LifetimeEnds when the function returnsUntil you free it
Failure modeStack overflow (crash)malloc returns NULL; leaks, fragmentation
Typical bugsReturning addresses of locals, huge arrays, deep recursionLeaks, use-after-free, double free

Stack frames

Each call pushes a frame: return address, saved registers, parameters that don’t fit in registers, and locals. Returning pops it — which is exactly why a pointer to a local is dangling after the return.

C
#include <stdio.h>

int depth(int n) {
    char pad[1024];                /* 1 KB of stack per call */
    pad[0] = (char)n;
    if (n == 0) return pad[0];
    return depth(n - 1) + 1;
}

int main(void) {
    printf("%d frames deep, fine\n", depth(100));    /* ~100 KB of stack */
    return 0;
}
Output
100 frames deep, fine

Where are string literals stored?

What is the difference between .data and .bss?

Why does the stack grow down and the heap grow up?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report