Understanding the memory layout of a C program is essential for writing efficient, secure, and reliable code. Unlike higher-level languages that abstract memory management, C provides direct control over memory, making it powerful but error-prone. This article explores the structure of a C programβs memory, its segments, common pitfalls, and best practices.
Overview of Memory Segments
A C programβs memory is divided into distinct segments, each serving a unique purpose. These segments are organized as follows in the virtual address space (from low to high addresses):
- Text Segment
- Data Segment
- BSS Segment
- Heap
- Stack
High Address
+------------------------+ β Stack (function calls, local vars)
| Stack |
|------------------------|
| Heap | β malloc(), calloc(), realloc()
|------------------------|
| BSS Segment (uninit) | β uninitialized global/static vars
|------------------------|
| Data Segment (init) | β initialized global/static vars
|------------------------|
| Text | β code (machine instructions)
+------------------------+
Low Address
High Memory Addresses
βββββββββββββββββββββββββββββββ
β Stack β β grows downward
β - Local variables β
β - Function call frames β
β - Return addresses β
βββββββββββββββββββββββββββββββ€
β Heap β β grows upward
β - malloc/calloc/realloc β
β - Managed via brk()/mmap() β
βββββββββββββββββββββββββββββββ€
β BSS Segment (Zero-inited) β
β - Uninitialized globals β
β - static int x; β
βββββββββββββββββββββββββββββββ€
β Data Segment (Init data) β
β - Initialized globals β
β - static int x = 5; β
βββββββββββββββββββββββββββββββ€
β Text Segment (Code) β
β - Machine instructions β
β - const string literals β
βββββββββββββββββββββββββββββββ
Low Memory Addresses
1οΈβ£ Text Segment (Code)
Purpose:
Stores executable machine instructions (compiled code).
Characteristics:
- Read-only: Prevents accidental code modification.
- Attempting to write causes segmentation fault.
- Shared: Reused across processes running the same binary to save memory.
- Contains string literals (e.g., βHello World!β).
Example:
printf("Hello"); // String "Hello" resides in the text segment.
2οΈβ£ Data Segment
Purpose:
Holds initialized
global and static variables.
Characteristics:
- Read-write: Variables can be modified during execution.
- Persistent: Exists for the program's entire lifecycle.
Example:
int globalVar = 42; // Stored in the data segment
static int staticVar = 5; // Also in the data segment
3οΈβ£ BSS Segment (Block Started by Symbol)
Purpose:
Store uninitialized global and static variables.
Characteristics:
- Zero-initialized: Set to
0
by the OS before program execution. - Reduces executable file size by not storing explicit zeroes.
Example:
int uninitGlobal; // BSS segment
static int uninitStatic; // BSS segment
4οΈβ£ Heap
Purpose:
Dynamically allocated memory (via malloc
, calloc
, realloc
).
Characteristics:
- Manual management: Allocated/freed by the programmer.
- Grows upwards: Exands toward higher addresses.
- Fragmentation risk: Inefficient use can lead to wasted memory.
- Memory must be freed manuallly.
Example:
int *arr = malloc(10 * sizeof(int)); // Heap allocation
free(arr); // Explicit deallocation required
Heap is allocated by the OS using:
brk()
β old methodmmap()
β more common now
5οΈβ£ Stack
Purpose:
Manages local variables, function parameters, and return addresses.
Characteristics:
- Automatic management: Allocated on function entry, freed on exit.
- Grows downwards: Expands toward lower addresses.
- Fixed size: Overflow cause stack exhaustion (e.g., infinite recursion).
Example:
void func() {
int localVar = 10; // Stack-allocated
} // Memory for localVar is freed automatically
6οΈβ£ Additional Memory Regions
- Command-Line Arguments & Environment Variables: Stored at the top of the stack or a dedicated region.
- Memory-Mapped Regions: Used for shared libraries, memory-mapped files, or dynamic linking.
Real Example in Code
Code:
#include <stdio.h>
#include <stdlib.h>
int global_data = 10;
static int static_data = 20;
static int static_uninit;
int main() {
int stack_var = 5;
char *heap_var = (char*)malloc(100);
printf("Text (main): %p\n", (void *)main);
printf("Global (data): %p\n", (void *)&global_data);
printf("Static (data): %p\n", (void *)&static_data);
printf("Static (bss): %p\n", (void *)&static_uninit);
printf("Stack: %p\n", (void *)&stack_var);
printf("Heap: %p\n", (void *)heap_var);
free(heap_var);
return 0;
}
Output:
Text (main): 0x1
Global (data): 0x6b0
Static (data): 0x6b4
Static (bss): 0x750
Stack: 0x500df8
Heap: 0x500e28
Leave a comment
Your email address will not be published. Required fields are marked *