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,
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.
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:
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
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().
Heap
+----------------------+
| Allocated Block |
+----------------------+
| Free Space |
+----------------------+
Unlike global variables, heap memory exists only until it is explicitly released.
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,
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.
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.
Although all three exist for the entire lifetime of the program, they are stored in different segments.
globalCounteris in the Data segment because it has an explicit initializer.totalStudentsis in the BSS segment because it has no explicit initializer.callCountis 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,
is stored in the Data segment and exists for the entire lifetime of the program.
Similarly,
is stored in the BSS segment and also exists until the program terminates.
On the other hand,
places x on the Stack.
The variable is created when func() is called and automatically destroyed when the function returns.
Likewise,
allocates memory on the Heap.
The allocated memory remains valid until it is explicitly released.
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.
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.
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()andfree(). - 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.
Relevant Links
C/C++ Material on this website
(such as memory management, pointers, embedded C programming etc.)