Skip to content

Memory Segmentation

This article is intended for intermediate and advanced C programmers. It explains how a C program is organized into different memory segments, the purpose of each segment, and how variables, functions, and dynamically allocated memory are placed within these regions during program execution.


1. What Is Memory Segmentation?

When a C program is executed, it is loaded into memory as a single process. Rather than placing everything into one large block of memory, the operating system organizes the program into several logical regions called memory segments.

Each segment has a specific purpose. For example:

  • executable instructions are stored separately from data,
  • global variables are kept in one region,
  • dynamically allocated memory is managed in another,
  • function calls and local variables use their own area.

1.1 Memory Layout in C Programs

A typical process memory layout is shown below.

Higher Memory Addresses
+---------------------------+
|          Stack            |
|   (Local Variables,       |
|    Function Calls)        |
+---------------------------+
|                           |
|       Free Memory         |
|                           |
+---------------------------+
|           Heap            |
|   (Dynamic Allocation)    |
+---------------------------+
|            BSS            |
| (Uninitialized Globals)   |
+---------------------------+
|           Data            |
| (Initialized Globals)     |
+---------------------------+
|           Text            |
| (Program Instructions)    |
+---------------------------+
Lower Memory Addresses

Each segment has different characteristics.

Segment Stores
Text Executable program instructions
Data Initialized global and static variables
BSS Uninitialized global and static variables
Heap Dynamically allocated memory
Stack Local variables, function parameters, and return addresses

1.2 Why Divide Memory into Segments?

Separating a program into multiple segments provides several advantages.

First, it allows different types of data to have different lifetimes.

For example:

  • Program instructions exist for the entire execution of the program.
  • Global variables also exist throughout the program's lifetime.
  • Local variables exist only while a function is executing.
  • Dynamically allocated memory exists until it is explicitly released.

Second, different segments can have different access permissions.

For example, the Text segment is typically read-only to prevent accidental modification of executable code, while the Data, Heap, and Stack segments are writable.

Finally, segmentation helps the operating system efficiently manage memory by organizing code, global data, dynamically allocated memory, and function call information into separate regions.

Although programmers usually do not control where these segments are located, understanding their purpose makes it much easier to reason about variable lifetime, memory allocation, stack overflows, heap usage, and many common programming errors.


2. Program Memory Segments

A typical C program is divided into five major memory segments.

Higher Memory Addresses
+---------------------------+
|          Stack            |
+---------------------------+
|                           |
|       Free Memory         |
|                           |
+---------------------------+
|           Heap            |
+---------------------------+
|            BSS            |
+---------------------------+
|           Data            |
+---------------------------+
|           Text            |
+---------------------------+
Lower Memory Addresses

Each segment serves a specific purpose and stores a particular type of information.

Segment Stores
Text Executable program instructions
Data Initialized global and static variables
BSS Uninitialized global and static variables
Heap Dynamically allocated memory
Stack Local variables, function parameters, and function call information

The following sections examine each of these segments in detail.

2.1 Text Segment

The Text segment, sometimes called the Code segment, stores the executable instructions of a program.

For example,

#include <stdio.h>

void greet(void)
{
    printf("Hello, World!\n");
}

int main(void)
{
    greet();
    return 0;
}

The compiled machine instructions for main() and greet() are stored in the Text segment.

Text Segment

+----------------------+
| main()               |
+----------------------+
| greet()              |
+----------------------+
| Library Functions    |
+----------------------+

The Text segment is typically read-only. Since executable code should never change while a program is running, marking this memory as read-only helps prevent accidental modification.


2.2 Data Segment

The Data segment stores all initialized global and static variables.

For example,

int counter = 100;
char grade = 'A';

static int limit = 50;

These variables have explicit initial values.

Data Segment

+----------------------+
| counter = 100        |
+----------------------+
| grade = 'A'          |
+----------------------+
| limit = 50           |
+----------------------+

Because the initial values must be available before main() begins execution, they are stored directly inside the executable file.

When the program starts, the operating system copies these initialized values from the executable into memory.


2.3 BSS Segment

BSS stands for Block Started by Symbol, a historical term originating from early assembler systems.

Like the Data segment, the BSS segment stores variables that exist for the entire lifetime of the program. The difference lies in whether the variables have an explicit initial value.

Data Segment BSS Segment
Initialized global variables Uninitialized global variables
Initialized static variables Uninitialized static variables

For example,

/* Data Segment */
int count = 100;
static int limit = 50;

/* BSS Segment */
int total;
static int index;

Although total and index do not have explicit initial values, the C language guarantees that they are initialized to zero before the program begins execution.

At this point, you might wonder:

Why have a separate BSS segment? Why not simply place these variables in the Data segment?

The answer is executable size.

Consider the following declaration.

char buffer[1024 * 1024];

This creates a 1 MB global array.

If this variable were stored in the Data segment, the executable file would need to contain one million zero bytes.

Executable

+----------------------+
| Text                 |
+----------------------+
| Data                 |
|                      |
| 1 MB of zeros        |
|                      |
+----------------------+

Those zeros serve no useful purpose because every byte has the same value.

Instead, the executable simply records:

Reserve 1 MB of zero-initialized memory.

When the program starts, the operating system allocates the memory and initializes it to zero.

Executable

+----------------------+
| Text                 |
+----------------------+
| Data                 |
+----------------------+
| BSS                  |
| Size = 1 MB          |
+----------------------+


Program Starts

Operating System

Allocate 1 MB
Initialize every byte to zero

As a result, the executable contains only the size of the BSS segment rather than storing millions of zero bytes.

For small variables the saving is insignificant, but for large global arrays, the difference can be enormous. A program containing

char image[100 * 1024 * 1024];

does not increase the executable by 100 MB. Instead, the executable merely records that 100 MB of zero-initialized memory should be reserved when the program starts.

This separation between the Data and BSS segments allows executables to remain much smaller while still guaranteeing that uninitialized global and static variables begin with a value of zero.


2.4 Heap Segment

The Heap is used for dynamic memory allocation.

Memory is obtained from the heap using functions such as malloc(), calloc(), and realloc().

int *numbers = malloc(10 * sizeof(int));
Heap

+----------------------+
| Allocated Block      |
+----------------------+
| Free Space           |
+----------------------+

Unlike global variables, heap memory exists only until it is explicitly released.

free(numbers);

Failing to release dynamically allocated memory results in memory leaks.


2.5 Stack Segment

The Stack stores information associated with function calls.

This includes:

  • local variables,
  • function parameters,
  • return addresses,
  • saved processor state.

For example,

void display(int value)
{
    int result = value * 2;

    printf("%d\n", result);
}

When display() is called, a new stack frame is created.

Stack

+----------------------+
| result              |
+----------------------+
| value               |
+----------------------+
| Return Address      |
+----------------------+

When the function returns, its stack frame is automatically removed, reclaiming all local variables without requiring the programmer to explicitly free them.


The following table summarizes the purpose of each memory segment.

Segment Typical Contents Lifetime
Text Program instructions Entire program
Data Initialized global and static variables Entire program
BSS Uninitialized global and static variables Entire program
Heap Dynamically allocated memory Until explicitly freed
Stack Local variables, parameters, and function call information Until the function returns

Understanding these segments helps explain where different kinds of variables are stored, why they have different lifetimes, and how memory is managed during program execution.


3. A Complete Example

Let's look at a complete program and identify where every variable and function is stored.

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

/* Data Segment */
int globalCounter = 100;

/* BSS Segment */
int totalStudents;

void display(void)
{
    /* Data Segment */
    static int callCount = 0;

    /* Stack */
    int localValue = 50;

    /* Heap */
    int *numbers = malloc(5 * sizeof(int));

    if (numbers != NULL)
    {
        numbers[0] = 10;

        printf("%d %d %d\n",
               globalCounter,
               localValue,
               numbers[0]);

        free(numbers);
    }

    callCount++;
}

int main(void)
{
    display();

    return 0;
}

This single program uses every major memory segment.

3.1 Where Is Everything Stored?

                 Program Memory

+------------------------------------------------+
| Stack                                          |
|----------------------------------------------- |
| localValue                                     |
| function parameters                            |
| return address                                 |
+------------------------------------------------+


+------------------------------------------------+
| Heap                                           |
|----------------------------------------------- |
| numbers -> dynamically allocated array         |
+------------------------------------------------+

+------------------------------------------------+
| BSS                                            |
|----------------------------------------------- |
| totalStudents                                  |
+------------------------------------------------+

+------------------------------------------------+
| Data                                           |
|----------------------------------------------- |
| globalCounter                                  |
| callCount (static)                             |
+------------------------------------------------+

+------------------------------------------------+
| Text                                           |
|----------------------------------------------- |
| main()                                         |
| display()                                      |
+------------------------------------------------+

The following table summarizes where each item is stored.

Item Memory Segment
main() Text
display() Text
globalCounter Data
totalStudents BSS
callCount Data
localValue Stack
numbers (pointer variable) Stack
Memory allocated by malloc() Heap

Notice the distinction between the pointer and the memory it references.

int *numbers = malloc(5 * sizeof(int));

The variable numbers itself is a local variable, so it is stored on the Stack.

The array created by malloc() is stored on the Heap.

Stack                      Heap

+-----------+             +----------------------+
| numbers --+-----------> | 10 |    |    |    |  |
+-----------+             +----------------------+

This is a common point of confusion. The location of a pointer variable is determined by where the pointer is declared, while the object it points to may reside in an entirely different memory segment.

Similarly, consider the following variables.

int globalCounter = 100;
int totalStudents;
static int callCount = 0;

Although all three exist for the entire lifetime of the program, they are stored in different segments.

  • globalCounter is in the Data segment because it has an explicit initializer.
  • totalStudents is in the BSS segment because it has no explicit initializer.
  • callCount is also in the Data segment because it is a static variable with an explicit initializer.

This example demonstrates how a single C program simultaneously uses all five major memory segments. Understanding where each variable resides makes it easier to reason about variable lifetime, memory allocation, and program behavior.

The next section discusses why memory segmentation matters in practice and how it influences program performance, debugging, and software design.


4. Why Memory Segmentation Matters

At first glance, memory segmentation may appear to be an implementation detail handled entirely by the operating system and compiler. However, understanding how a program is organized in memory helps explain many aspects of C programming, including variable lifetime, memory allocation, program performance, and common programming errors.

4.1 Understanding Variable Lifetime

The memory segment in which a variable is stored determines how long it exists.

For example,

int global = 10;

is stored in the Data segment and exists for the entire lifetime of the program.

Similarly,

int counter;

is stored in the BSS segment and also exists until the program terminates.

On the other hand,

void func(void)
{
    int x = 5;
}

places x on the Stack.

The variable is created when func() is called and automatically destroyed when the function returns.

Likewise,

int *p = malloc(sizeof(int));

allocates memory on the Heap.

The allocated memory remains valid until it is explicitly released.

free(p);

Understanding where a variable is stored makes its lifetime much easier to understand.


4.2 Understanding Common Programming Errors

Many common programming errors are directly related to memory segments.

Returning the address of a local variable is a classic example.

int *func(void)
{
    int value = 100;

    return &value;      /* Wrong */
}

The variable value resides on the Stack.

Once the function returns, its stack frame is removed, leaving the returned pointer pointing to invalid memory.

Similarly, forgetting to free dynamically allocated memory causes a memory leak.

int *p = malloc(sizeof(int));

/* Missing free(p); */

Since heap memory is not released automatically, repeated allocations eventually exhaust the available memory.


4.3 Debugging Programs

Knowing where objects are stored often makes debugging much easier.

For example,

  • a segmentation fault immediately after returning from a function may indicate a dangling pointer to Stack memory,
  • increasing memory usage over time often suggests a Heap memory leak,
  • modifying a string literal may fail because string literals are typically stored in read-only memory.

Recognizing the role of each segment helps narrow down the source of these problems.


4.4 Embedded Systems

Memory segmentation is especially important in embedded systems.

Unlike desktop computers, embedded devices often have limited RAM and Flash memory.

For example,

  • program instructions are stored in Flash memory,
  • global variables occupy RAM,
  • the Heap and Stack share the remaining available memory.
Flash Memory

+----------------------+
| Text                 |
+----------------------+

RAM

+----------------------+
| Data                 |
+----------------------+
| BSS                  |
+----------------------+
| Heap                 |
|        ↑             |
|                      |
|        ↓             |
| Stack                |
+----------------------+

If the Heap grows upward while the Stack grows downward, the two regions may eventually collide, causing unpredictable program behavior.

Understanding memory segmentation is therefore essential when designing reliable embedded software.


Although the compiler and operating system manage these memory segments automatically, every C programmer benefits from understanding how they work. It explains where variables are stored, how long they remain valid, and why certain programming errors occur.

The next section summarizes the key concepts discussed in this article.


5. Summary

Memory segmentation is a fundamental concept that describes how a C program is organized in memory during execution. Rather than treating all memory as a single block, the operating system divides it into several segments, each serving a specific purpose.

The major memory segments are:

  • Text – stores the executable instructions of the program.
  • Data – stores initialized global and static variables.
  • BSS – stores uninitialized global and static variables, which are automatically initialized to zero when the program starts.
  • Heap – stores dynamically allocated memory managed by functions such as malloc() and free().
  • Stack – stores function call information, including local variables, parameters, and return addresses.

A key distinction between the Data and BSS segments is that initialized variables must have their initial values stored in the executable, whereas uninitialized variables require only memory to be reserved and zero-initialized at program startup. This design significantly reduces the size of executable files.

Understanding memory segmentation helps explain:

  • where different kinds of variables are stored,
  • how long variables remain valid,
  • how dynamic memory allocation works,
  • why stack-related and heap-related programming errors occur,
  • how operating systems and compilers organize a program in memory.

Although memory segmentation is managed automatically by the compiler, linker, loader, and operating system, it forms the foundation for many important topics in C programming, including variable lifetime, memory allocation, debugging, embedded systems, and program performance.


C/C++ Material on this website

(such as memory management, pointers, embedded C programming etc.)