Skip to main content

The Virtual Address Space: Text, Data, BSS, Heap, Stack

What This Concept Is

When a process runs, the operating system gives it a private virtual address space. Not all of that range is usable; the OS lays out a handful of named regions, each with its own purpose and lifetime:

  • text (also .text): the program's machine code. Read-only and executable.
  • rodata (.rodata): read-only constants such as string literals and const globals.
  • data (.data): initialized writable globals and static variables.
  • bss (.bss): zero-initialized writable globals and static variables. Takes zero space in the binary; the OS fills it with zeros at load time.
  • heap: dynamically allocated memory (malloc / free). Grows upward (toward higher addresses) on most systems.
  • stack: per-thread call stack for local variables, arguments, return addresses. Grows downward on most systems.

Why It Matters Here

The segment a variable lives in determines:

  • how long it lives (function call, program run, or until free)
  • whether you can write to it (writing to .rodata traps)
  • whether its address is predictable (stack is not; .data often is)
  • who is responsible for releasing it (heap requires an explicit free)

Debuggers, profilers, and linkers all speak in these segment names.

Concrete Example

Typical Linux 64-bit layout, high to low:

      high addresses
+----------------------+
| (kernel, not shown) |
+----------------------+
| Stack (grows down) | locals, saved rbp, return addresses
| | |
| v |
+----------------------+
| (unmapped gap) |
+----------------------+
| Memory-mapped region | shared libraries, mmap'd files
+----------------------+
| (unmapped gap) |
+----------------------+
| ^ |
| | |
| Heap (grows up) | malloc'd blocks
+----------------------+
| .bss (zeroed) | uninitialized globals and static
+----------------------+
| .data (writable) | initialized globals and static
+----------------------+
| .rodata (read only) | string literals, const
+----------------------+
| .text (executable) | machine code
+----------------------+
low addresses

A tiny C program placing a variable in each segment:

#include <stdlib.h>

const char *msg = "hi"; /* msg in .data; "hi" in .rodata */
int g_init = 7; /* .data */
int g_zero; /* .bss */

int main(void) {
static int s = 3; /* .data */
int local = 1; /* stack */
int *heap = malloc(sizeof *heap); /* heap */
(void)msg; (void)g_init; (void)g_zero; (void)s; (void)local;
free(heap);
return 0;
}

Common Confusion / Misconception

"The heap and stack are next to each other." On modern 64-bit systems they are separated by a huge unmapped region. The old "the heap grows until it meets the stack" image is simplified history.

"String literals are on the stack." No. char *s = "hello"; places the string in .rodata and s on the stack. Writing s[0] = 'H'; is undefined behavior. In contrast, char s[] = "hello"; copies it into a stack-allocated array that is writable.

"Globals are on the heap because they outlive functions." Globals are in .data or .bss. The heap is strictly what malloc returns.

How To Use It

For every variable in a program:

  1. Ask: where is it stored? stack, heap, .data, .bss, or .rodata?
  2. Ask: who owns it? (caller's frame, this function's frame, the whole process)
  3. Ask: when is it freed? (automatic on function return, explicit free, or program exit)
  4. If you need its address to survive the function, it cannot be on the stack.

Check Yourself

  1. Why is return &local; a bug?
  2. What is the difference between static int x = 0; and int x = 0; inside a function?
  3. Where does a string literal "hello" live, and why can you not modify it?

Mini Drill or Application

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

int g_init = 7;
int g_zero;

int main(void) {
int local = 1;
int *heap = malloc(sizeof *heap);
*heap = 2;
printf("g_init=%p g_zero=%p\n", (void *)&g_init, (void *)&g_zero);
printf("main =%p local =%p heap =%p\n",
(void *)&main, (void *)&local, (void *)heap);
free(heap);
return 0;
}

Build: gcc -Wall -Wextra -o segments segments.c. Run it twice. The stack and heap addresses may change between runs because of address-space layout randomization; the .data / .text addresses stay similar.

Read This Only If Stuck