Stack vs Heap — Process Memory Layout
C++ programs have several distinct memory regions. The **stack** holds local variables and function call frames — allocation/deallocation is automatic (push/pop of stack pointer), extremely fast (O(1)), but limited in size (~1–8 MB). The **heap** holds dynamically allocated memory — allocation (`new`/`malloc`) is slower, size limited only by physical RAM, but you must manually manage lifetime. The **data segment** holds global and static variables. Understanding this layout is the foundation of every memory bug you'll ever debug.
Memory Layout — Stack, Heap, Data & Text Segments
#include <iostream>
#include <new> // std::bad_alloc
#include <cstdlib> // malloc
// ── Process memory regions (low → high address on Linux x86-64) ──
// [TEXT] | Machine code (read-only) — functions, string literals
// [DATA] | Initialised globals & statics: int g = 42;
// [BSS] | Uninitialised globals & statics: int g; (zero-init)
// [HEAP] | new/malloc — grows upward
// | (large gap — unmapped)
// [STACK] | local variables, call frames — grows downward
// [KERNEL] | OS memory (inaccessible to user code)
int global_var = 100; // .DATA segment
int uninit_global; // .BSS segment (zero-initialised)
void demonstrate_layout() {
int stack_int = 5; // STACK
static int static_local = 10; // DATA (not stack!)
int* heap_int = new int(42); // HEAP
int* heap_arr = new int[100]; // HEAP — 100 ints
std::cout << "Stack var address: " << (void*)&stack_int << "
";
std::cout << "Heap int address: " << (void*)heap_int << "
";
std::cout << "Global var address: " << (void*)&global_var << "
";
std::cout << "Static var address: " << (void*)&static_local<<"
";
// Stack vars: nearby addresses (within ~KB)
// Heap allocs: distant addresses (different segment)
// Globals/statics: near each other (data segment)
delete heap_int; // release single object
delete[] heap_arr; // release array (MUST use delete[])
// stack_int: automatically destroyed when demonstrate_layout returns
}
// ── Stack overflow — too deep recursion or too large locals ──
// void stack_overflow() {
// char big_array[1024*1024]; // 1MB on stack — likely crashes
// stack_overflow(); // infinite recursion → overflow
// }
// ── Heap allocation failure ───────────────────────────────────
void allocation_demo() {
try {
// new throws std::bad_alloc on failure (out of memory)
int* huge = new int[1000000000000LL]; // 4TB — will fail
delete[] huge;
} catch (const std::bad_alloc& e) {
std::cerr << "Allocation failed: " << e.what() << "
";
}
// nothrow version — returns nullptr on failure
int* p = new(std::nothrow) int[1000000000000LL];
if (!p) {
std::cerr << "nothrow allocation returned nullptr
";
}
}
int main() {
demonstrate_layout();
allocation_demo();
return 0;
}Common Mistakes
- `delete[]` vs `delete` mismatch — `new[]` MUST be paired with `delete[]`. Using `delete` on a `new[]` allocation is undefined behaviour (partial destructor calls, wrong deallocation). The compiler cannot catch this.
- Stack allocation of very large objects — `int arr[10000000]` (40 MB) on the stack causes immediate stack overflow. Large data must be heap-allocated. Rule of thumb: anything > 64KB should use heap.
- Assuming heap memory is zero-initialised — `new int[n]` gives uninitialised values. `new int[n]()` (with parentheses) value-initialises to zero. Or use `std::vector<int>(n)` which always zero-initialises.
Tip
Tip
Practice Stack vs Heap Process Memory Layout in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
The four pillars of Object-Oriented Programming in C++
Practice Task
Note
Practice Task — (1) Write a working example of Stack vs Heap Process Memory Layout from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Stack vs Heap Process Memory Layout is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready cpp code.
Key Takeaways
- C++ programs have several distinct memory regions.
- `delete[]` vs `delete` mismatch — `new[]` MUST be paired with `delete[]`. Using `delete` on a `new[]` allocation is undefined behaviour (partial destructor calls, wrong deallocation). The compiler cannot catch this.
- Stack allocation of very large objects — `int arr[10000000]` (40 MB) on the stack causes immediate stack overflow. Large data must be heap-allocated. Rule of thumb: anything > 64KB should use heap.
- Assuming heap memory is zero-initialised — `new int[n]` gives uninitialised values. `new int[n]()` (with parentheses) value-initialises to zero. Or use `std::vector<int>(n)` which always zero-initialises.