C++ Stack vs Heap | Why Recursion Crashes and Real Stack Overflow Cases

C++ Stack vs Heap | Why Recursion Crashes and Real Stack Overflow Cases

이 글의 핵심

Stack vs heap in C++: recursion crashes, stack overflow, size limits, when to use vector on the heap, and how stack frames work—foundation for smart pointers and RAII.

Introduction: Crashed by stack overflow

Why the process died — recursion trap

You implemented Fibonacci recursively. n = 10 worked; n = 100000 killed the process with no helpful message. The stack is a small, fast region—like a narrow workbench: deep recursion stacks many frames, each holding locals, until the bench overflows.

The demo below stacks ~4KB per call (int cache[1000]) and explores two branches—stack usage explodes. fibonacci(100000) exceeds the process’s stack limitstack overflow. (The cache array is unused in logic—it illustrates per-frame stack cost.)

// g++ -std=c++17 -o fib fib.cpp && ./fib
#include <iostream>

int fibonacci(int n) {
    int cache[1000];  // ~4KB per frame

    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

int main() {
    std::cout << fibonacci(100000);  // crashes — stack overflow
    return 0;
}

Debugging path:

  1. Logic bug? → not here
  2. Leak? → Valgrind says no
  3. Stack overflow — stack limit exceeded

Takeaway: know stack vs heap; huge locals and deep recursion are dangerous on the default stack.

After reading:

  • How stack and heap behave
  • How to avoid stack overflow
  • When to use stack vs heap
  • How layout fits later smart pointers and RAII

Previous: Compilation process


Table of contents

  1. Process memory layout
  2. Stack memory
  3. Heap memory
  4. Stack vs heap performance
  5. Selection guide
  6. Complete examples
  7. Common memory errors
  8. Debugging tips
  9. Production patterns

1. C++ process memory (simplified)

The OS maps text (code), data/BSS (globals), heap (grows up), and stack (grows down). If stack and heap meet, you’re in trouble.

flowchart TB
  subgraph addr["Address direction (low → high)"]
    direction TB
    T[Text code]
    D[Global/static Data, BSS]
    H[Heap ↑]
    free[Free space]
    S[Stack ↓]
  end
  T --> D --> H --> free --> S
  style H fill:#e1f5fe
  style S fill:#fff3e0
RegionRoleLifetime
StackLocals, args, return addressesUntil scope returns
Heapnew / mallocUntil freed or process exit

Why stacks are small: one stack per thread; deep recursion and large frames consume it quickly—typically ~1–8MB per platform default.


2. Stack memory

Fast, automatic, limited. Each function call pushes a stack frame (locals, saved state).

Advantages:

  1. Fast—just move the stack pointer
  2. Automatic—destroyed on scope exit
  3. Cache-friendly—contiguous

Disadvantages:

  1. Small—don’t put megabytes as int huge[1000000] local
  2. Lifetime tied to scope—returning address of a local → dangling pointer

Stack overflow examples

Huge local array → use std::vector on the heap.

Deep recursion with big locals → reduce depth, move arrays to heap, or iterate.

Mitigation (last resort):

ulimit -s 16384   # Linux: 16MB (example)

Root fix: don’t rely on giant stacks—heap or iteration.


3. Heap memory

Flexible size and lifetime; you (or smart pointers) must free it.

Pros: large allocations, outliving scope, polymorphic arrays
Cons: slower, fragmentation risk, manual correctness without RAII


4. Stack vs heap performance

Microbenchmarks often show stack allocation ~orders of magnitude faster than new/delete in tight loops—don’t micro-optimize hot loops with per-iteration heap allocs.


5. Selection guide

  • Small, local, short-lived → stack
  • Large, shared, runtime-sized, polymorphic → heap + vector/unique_ptr

6. Patterns

  • unique_ptr for sole ownership
  • shared_ptr when shared (watch cycles → weak_ptr)
  • RAII wrappers for buffers and files

7. Common errors

  1. Stack overflow — huge locals / deep recursion
  2. Dangling pointers — address of stack local returned
  3. delete on stack memory — undefined
  4. Double free / use-after-free
  5. Leaknew without delete on all paths

8. Debugging tips

  • Valgrind / ASan (-fsanitize=address) for heap corruption and leaks
  • GDB + core for post-mortem stack traces
  • ulimit -s to inspect stack limit

9. Production patterns

  • Object pools to cut repeated new/delete
  • Stack allocators / arenas for frame-scoped bump allocation
  • Default to unique_ptr, shared_ptr only when needed

FAQ

Biggest difference stack vs heap?

A. Stack auto-managed, tiny, fast; heap flexible and large but manual (or smart pointers).

How to prevent stack overflow?

A. Avoid huge stack arrays; limit recursion depth; use heap containers; iterate.

Stack vs heap speed?

A. Microbenchmarks often show 50–100× gap for raw alloc vs stack pointer bump—measure your workload.

Why not return address of stack variable?

A. After return the frame is gone—dangling pointerUB.


  • Memory leaks
  • Smart pointers
  • RAII

Keywords

C++ stack vs heap, stack overflow, memory layout, dangling pointer, heap allocation, recursion

Summary

  • Stack: fast, automatic, limited
  • Heap: flexible, must manage (prefer smart pointers)
  • Default: small locals on stack; large data on heap
  • Watch recursion and large arrays on the stack

Next: Memory leaks #6-2

References

Practical tips

Debugging

  • Enable warnings; reproduce minimally

Performance

  • Profile before optimizing

  • Stack vs heap (alternate slug)
  • Valgrind