본문으로 건너뛰기 C++ Stack Overflow: Recursion, Large Locals, and How to Fix

C++ Stack Overflow: Recursion, Large Locals, and How to Fix

C++ Stack Overflow: Recursion, Large Locals, and How to Fix

이 글의 핵심

Why stack overflow happens: infinite recursion, huge stack arrays, deep recursion. ulimit, /STACK, heap allocation, iterative algorithms, and tail-call basics.

Stack exhaustion often shows up as a segfault—compare [segfault (deep dive)](/en/blog/cpp-error-02-segmentation-fault/ and segfault (checklist).

Introduction: “My recursive function crashes”

“Segfault but I’m not using pointers”

Stack overflow happens when stack memory is exhausted. Common causes: infinite recursion, very large locals, and very deep recursion.

// 실행 예제
void foo() {
    foo();
}
int main() {
    foo();
}
// Linux/macOS: SIGSEGV; Windows: stack overflow

This article covers:

  • Four major causes
  • Recursion depth limits
  • Adjusting stack size (last resort)
  • Moving work to the heap
  • Tail recursion and iterative forms

Table of contents

  1. What is stack overflow?
  2. Four major causes
  3. Limiting recursion depth
  4. Stack size settings
  5. Heap allocation
  6. Tail recursion
  7. Summary

1. What is stack overflow?

Stack memory

Each function call creates a stack frame for locals and return addresses. Typical default stack limits (order of magnitude):

  • Linux: ~8 MB
  • Windows: ~1 MB (varies)
  • macOS: ~8 MB When recursion or locals exceed the limit, the program crashes.

2. Four major causes

1. Infinite recursion

int factorial(int n) {
    return n * factorial(n - 1); // missing base case
}

Fix: Base case.

int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

2. Large local arrays

void process() {
    int bigArray[1000000]; // ~4 MB on stack—may overflow
}

Fix:

void process() {
    std::vector<int> bigArray(1000000);
}

3. Very deep naive recursion (e.g. Fibonacci)

Fix: Iteration or memoization.

4. Deep call chains with large frames

Move big temporaries to the heap or reuse buffers.

3. Limiting recursion depth

int factorial(int n, int depth = 0) {
    const int MAX_DEPTH = 1000;
    if (depth > MAX_DEPTH) {
        throw std::runtime_error("Recursion too deep");
    }
    if (n <= 1) return 1;
    return n * factorial(n - 1, depth + 1);
}

Prefer loops when depth can be large.

4. Stack size

Linux: ulimit

ulimit -s       # size in KB
ulimit -s 16384 # e.g. 16 MB
ulimit -s unlimited  # use with care

Windows linker

Linker → System → Stack Reserve (Visual Studio), or:

/STACK:16777216

CMake (examples)

# Linux ELF (toolchain-specific)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,stack-size=16777216")
# MSVC
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:16777216")

5. Heap instead of stack

void process() {
    std::vector<int> big(1000000);
    // or
    auto big = std::make_unique<int[]>(1000000);
}

Tree traversal: explicit std::stack

Replace deep recursion with an iterative walk using a container on the heap when depth is unbounded (e.g. deeply nested JSON).

6. Tail recursion

Non-tail (work after the call):

int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

Tail form (call is last):

int factorialTail(int n, int acc = 1) {
    if (n <= 1) return acc;
    return factorialTail(n - 1, n * acc);
}

Inspect assembly at -O2 to see if the compiler optimized to a loop.

Case studies (sketches)

JSON parsing

Deep nesting can blow the stack with naive recursion; use an explicit stack or iteration.

Directory walk

Replace deep filesystem recursion with a directory stack/std::stack of paths.

Summary

Checklist

  • Base case for every recursive function?
  • Avoid multi-megabyte stack arrays?
  • Recursion depth bounded or converted to iteration?
  • Nested calls not multiplied by huge locals?

Mitigation priority

  1. Correct base cases
  2. Heap for large buffers
  3. Iterative algorithms / explicit stacks
  4. Tail recursion where applicable
  5. Increase stack only as a temporary measure

Rules

  1. Large arrays → vector / unique_ptr
  2. Guard recursion depth when needed
  3. Deep or unbounded recursion → iteration
  4. Consider tail-call patterns with optimizations enabled


Keywords

stack overflow, infinite recursion, recursion depth, large array, stack size, ulimit

Practical tips

Debugging

  • Add a depth counter during investigation.
  • Use backtrace to see repeated frames.
  • Valgrind/ulimit experiments can clarify stack usage.

Performance

  • Tail recursion may optimize at -O2+.
  • Loops are often faster than heavy recursion.
  • Memoization reduces call count.

Reviews

  • Verify termination for recursive functions.
  • Flag large stack allocations.
  • Question unbounded recursion depth.

Closing

Stack overflow is preventable: control recursion, avoid huge stack frames, and use the heap for large data. Increasing stack size is a last resort; fix the algorithm and memory placement first. Next: Pair this with deeper material on recursion vs iteration and tail-call optimization in your favorite C++ resource.


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Why stack overflow happens: infinite recursion, huge stack arrays, deep recursion.

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers C++, Stack overflow, Recursion, Crash, Memory, Debugging.