Stack vs heap — new/delete & memory layout
Scenario
You declare `int data[1000000];` inside a function to hold some calculations. You compile the code, but when you run it, the program instantly crashes with a 'Segmentation fault' before executing a single line of your logic.
Why did simply declaring an array cause an immediate crash?
Mental model
The Stack is a fast, tiny notepad on your desk; you use it for quick, temporary math, and you tear off the page when you're done. The Heap is a giant warehouse; you have to explicitly call a manager (`new`) to reserve space, and call them again (`delete`) to free it up.
Understanding where memory lives is the core of C++. The Stack is managed automatically by the compiler; it is extremely fast, cache-friendly, but very limited in size (often just a few megabytes). The Heap (dynamic memory) is massive, but allocating and deallocating memory is manual, slower, and prone to fragmentation.
Explanation
When a C++ program runs, memory is divided into segments. The most important for developers are the Stack and the Heap.
**The Stack (Automatic Memory):**
Every time a function is called, a 'stack frame' is pushed onto the Stack. It holds all local variables and the return address. When the function finishes, the frame is popped, instantly destroying all local variables. Allocation is simply moving a pointer, making it incredibly fast. However, if you declare an array too large (e.g., a million ints), it exceeds the OS-imposed stack limit, causing a Stack Overflow crash.
**The Heap (Dynamic Memory):**
When you need large amounts of data, or data that must outlive the function that created it, you use the Heap. You request memory using the `new` keyword, which returns a pointer to the allocated space. This memory stays reserved until you explicitly release it using `delete` (or `delete[]` for arrays). Because the OS must search for continuous free blocks in the warehouse, heap allocations are much slower than stack allocations.
Code examples
Stack vs Heap Allocation
#include <iostream>
void processData() {
// STACK: Fast, automatic, but size is fixed at compile time.
// Destroyed automatically when processData() ends.
int localArray[10];
// HEAP: Slower allocation, but can be massive and dynamic.
// We explicitly request memory using 'new'.
int size = 1000000;
int* dynamicArray = new int[size];
// Do work...
// We MUST explicitly free heap memory when done, or it leaks.
// Notice the [] because we allocated an array.
delete[] dynamicArray;
}
int main() {
processData();
return 0;
}
The pointer `dynamicArray` itself lives on the stack, but the 1 million integers it points to live on the heap. When the function ends, the pointer is destroyed, but the heap memory remains unless explicitly deleted.
Key points
- Stack is default: Always prefer stack allocation (local variables) unless the object is too large or needs to outlive the function scope.
- new requires delete: Every call to `new` must be matched by a corresponding `delete` to prevent resource leaks.
Common mistakes
- Returning a pointer to a stack variable: Writing `int* getInt() { int x = 5; return &x; }` is a catastrophic bug. `x` lives on the stack and is destroyed the moment the function returns. The caller receives a pointer to dead memory.
Recall questions
- What happens if you allocate memory with `new[]` but free it with `delete` instead of `delete[]`?
- Why is stack allocation significantly faster than heap allocation?
- What is memory fragmentation and why does it occur on the heap but not the stack?
Questions & answers
A candidate writes a recursive depth-first search (DFS) for a graph with 10 million nodes, storing the visited array locally in the recursive function. It crashes in production. Why?
Every recursive call pushes a new frame onto the stack. At 10 million depth, this exhausts the thread's stack space, causing a Stack Overflow. The fix is to use an iterative DFS with a manually allocated stack on the heap (using `new`) to hold the nodes to visit, completely bypassing the OS stack limits.
Approach: Recognize recursion depth limits and stack memory constraints.
A function creates a large lookup table using `int* table = new int[1000];` and returns the pointer. What happens if the caller loses the pointer before calling `delete[]`?
The memory is permanently leaked. The heap memory remains reserved but completely inaccessible until the program terminates.
Approach: Understand the manual lifecycle of heap allocations.
Continue learning
Previous: Functions — pass by value, reference & pointer