C Memory Layout

When a C program runs, the operating system assigns it a block of memory. This memory is not used randomly — it is organized into distinct regions, each with a specific purpose. Understanding the memory layout of a C program helps you write better code, avoid bugs like buffer overflows and memory leaks, and understand why certain variables behave the way they do.

The Five Segments of a C Program's Memory


High Address
+---------------------------+
|   Stack                   |  ← Local variables, function calls
|   (grows DOWNWARD ↓)      |
+---------------------------+
|           ↕               |  ← Free space between stack and heap
|   Heap                    |
|   (grows UPWARD ↑)        |  ← malloc(), calloc() — dynamic memory
+---------------------------+
|   BSS Segment             |  ← Uninitialized global/static variables
+---------------------------+
|   Data Segment            |  ← Initialized global/static variables
+---------------------------+
|   Text (Code) Segment     |  ← Your program's compiled instructions
+---------------------------+
Low Address

1. Text Segment (Code Segment)

The text segment holds the compiled machine code of your program — the actual CPU instructions that execute when your program runs. This segment is read-only to prevent the program from accidentally overwriting its own instructions.

  • Contains: compiled function code, program logic
  • Read-only: yes (attempts to write here cause a segfault)
  • Example: the body of main(), your functions
int add(int a, int b) {
    return a + b;   // this instruction lives in the text segment
}

2. Data Segment (Initialized Data)

The data segment stores global and static variables that have an explicit initial value set by the programmer.

  • Contains: initialized global variables, initialized static variables
  • Lifetime: entire program execution
  • Writable: yes
int count = 10;           // stored in data segment (initialized global)
static float rate = 3.5;  // stored in data segment (initialized static)

int main() {
    // count and rate are accessible anywhere
    return 0;
}

3. BSS Segment (Uninitialized Data)

The BSS segment (Block Started by Symbol) stores global and static variables that are declared but not given an initial value. The OS automatically sets them to zero at program start.

  • Contains: uninitialized global variables, uninitialized static variables
  • Default value: all zeros (set by OS before program starts)
  • Lifetime: entire program execution
int score;          // stored in BSS — automatically 0
static int level;   // stored in BSS — automatically 0
char name[100];     // stored in BSS — all bytes are 0

int main() {
    printf("%d\n", score);   // prints 0 (BSS guarantees zero initialization)
    return 0;
}

4. Heap Segment

The heap is the region for dynamic memory allocation. Memory on the heap is allocated and freed manually by the programmer using malloc(), calloc(), realloc(), and free().

  • Contains: memory allocated at runtime with malloc/calloc
  • Lifetime: from allocation until freed (or program ends)
  • Grows: upward toward high addresses
  • Risk: forgetting to call free() causes memory leaks
#include <stdlib.h>

int main()
{
    int *ptr = (int*) malloc(5 * sizeof(int));   // allocates 20 bytes on heap
    ptr[0] = 100;
    ptr[1] = 200;
    // ... use the memory ...
    free(ptr);   // release heap memory — must always do this!
    return 0;
}

5. Stack Segment

The stack stores local variables, function parameters, return addresses, and saved register values for each active function call. The stack works as a LIFO (Last In, First Out) structure — when a function is called, a stack frame is pushed; when it returns, the frame is popped and that memory is freed automatically.

  • Contains: local variables, function arguments, return address
  • Lifetime: from function entry to function exit
  • Grows: downward toward low addresses
  • Risk: too many nested calls or large local arrays cause stack overflow
void display(int n)   // n is stored on the stack
{
    int temp = n * 2;   // temp is stored on the stack
    printf("%d\n", temp);
    // temp and n are destroyed when display() returns
}

int main()
{
    int x = 10;   // x is on the stack
    display(x);
    return 0;
}

Stack Frame Diagram


When main() calls display(10):

Stack (top = lower address):
+-------------------------+
| display's frame:        |
|   n    = 10             |
|   temp = 20             |
|   return address        |
+-------------------------+
| main's frame:           |
|   x    = 10             |
|   return address        |
+-------------------------+

When display() returns:
  display's frame is POPPED and that memory is freed immediately.

Stack vs Heap — Key Differences

FeatureStackHeap
ManagementAutomatic (compiler manages)Manual (programmer manages)
SpeedVery fastSlower (OS involvement)
SizeLimited (typically 1–8 MB)Large (limited by system RAM)
LifetimeUntil function returnsUntil explicitly freed
RiskStack overflow (too many calls)Memory leak (forgetting free)
Grows towardLow addresses (downward)High addresses (upward)

Identifying Where Variables Live

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

int globalInit = 5;       // Data segment
int globalUninit;         // BSS segment

void demo()
{
    int localVar = 10;                          // Stack
    static int staticVar = 20;                  // Data segment
    int *heapVar = (int*)malloc(sizeof(int));   // Heap
    *heapVar = 30;

    printf("local  (Stack): %p\n", (void*)&localVar);
    printf("static (Data) : %p\n", (void*)&staticVar);
    printf("heap   (Heap) : %p\n", (void*)heapVar);

    free(heapVar);
}

int main()
{
    demo();
    return 0;
}

Common Memory Problems

ProblemCauseRegion
Stack OverflowInfinite recursion or very large local arraysStack
Memory Leakmalloc without freeHeap
Dangling PointerUsing a pointer after free()Heap
Buffer OverflowWriting beyond array boundsStack or Heap
Segmentation FaultWriting to read-only memory or NULL pointerText / BSS

Summary

A C program's memory is divided into five segments. The text segment holds compiled code and is read-only. The data segment holds initialized global and static variables. The BSS segment holds uninitialized global and static variables, all initialized to zero by the OS. The heap holds dynamically allocated memory managed manually by the programmer — always pair malloc with free. The stack holds local variables and function call information, managed automatically — each function call creates a stack frame that is destroyed on return. Understanding these segments prevents memory bugs and helps write safer, more efficient C programs.

Leave a Comment

Your email address will not be published. Required fields are marked *