Stack, Heap & Memory Layout
How your program's memory is organized — and why it matters for every pointer you write
Quick check before you start: Can you draw a box for “stack” and a box for “heap” and say which grows up vs. down? If yes, skip to Stack Frames. If not, read on.
Practice this topic: Memory Layout skill drill
After this lesson, you will be able to:
- Name the five segments of a C program’s memory layout
- Explain the difference between stack and heap lifetime
- Describe what a stack frame contains
- Distinguish where local, global, and dynamically allocated variables live
The Five Memory Segments
When the OS loads your compiled C program, it carves out five regions of virtual memory:
| Segment | Contents | Lifetime |
|---|---|---|
| Text | Machine code (your compiled instructions) | Entire program |
| Data | Initialized global/static variables | Entire program |
| BSS | Uninitialized global/static variables (zeroed) | Entire program |
| Stack | Local variables, function parameters, return addresses | Per function call |
| Heap | Dynamically allocated memory (calloc, realloc) |
Until you free it |
The text segment is read-only. The data and BSS segments are fixed at load time. The stack and heap are where the action happens at runtime.
High address
┌──────────────┐
│ Stack │ ← grows downward
│ ↓ │
│ │
│ ↑ │
│ Heap │ ← grows upward
├──────────────┤
│ BSS │
├──────────────┤
│ Data │
├──────────────┤
│ Text │
└──────────────┘
Low address
Stack Frames
Every time you call a function, the system pushes a stack frame onto the stack. That frame holds:
- The function’s local variables
- The parameters passed to the function
- The return address (where to go back after the function ends)
When the function returns, the frame is popped. The memory is gone. This is why returning a pointer to a local variable is a bug — the memory it points to no longer belongs to you.
int* broken(void) {
int x = 42;
return &x; // WARNING: x dies when broken() returns
}
Your compiler will warn you about this. Listen to it.
Local vs. Global Variables
A local variable lives on the stack inside its function’s frame. It is created when the function is called and destroyed when the function returns.
A global variable lives in the data or BSS segment. It exists for the entire program. If you initialize it (int count = 10;), it goes in data. If you do not (int count;), it goes in BSS and is automatically zeroed.
Stack vs. Heap Lifetime
This is the core distinction:
- Stack memory is automatic. You do not allocate it or free it. It disappears when the function returns.
- Heap memory is manual. You allocate it with
callocand you must free it withfree. It survives until you say otherwise.
In Java, the garbage collector handles heap cleanup. In C, you are the garbage collector.
void example(void) {
int local = 5; // stack — dies at }
int *heap = calloc(1, sizeof(int)); // heap — lives until free()
*heap = 5;
free(heap); // now it dies
}
If you forget free(heap), that memory leaks. The OS reclaims it when your program exits, but in a long-running program (a server, a game), leaks accumulate and eventually crash the process.
What Comes Next
You know where memory lives. Next, you will learn how to allocate and free heap memory yourself using calloc, realloc, and free.