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)](/en/blog/cpp-error-27-segmentation-fault/.
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
- What is stack overflow?
- Four major causes
- Limiting recursion depth
- Stack size settings
- Heap allocation
- Tail recursion
- 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
- Correct base cases
- Heap for large buffers
- Iterative algorithms / explicit stacks
- Tail recursion where applicable
- Increase stack only as a temporary measure
Rules
- Large arrays → vector / unique_ptr
- Guard recursion depth when needed
- Deep or unbounded recursion → iteration
- Consider tail-call patterns with optimizations enabled
Related posts (internal)
- Memory basics: stack vs heap
- Segmentation fault guide
- This article’s short segfault companion
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/
ulimitexperiments 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.
More related posts
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Why stack overflow happens: infinite recursion, huge stack arrays, deep recursion. ulimit, /STACK, heap allocation, iter… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- [C++ Segmentation Fault: Five Causes and Debugging with GDB,](/en/blog/cpp-error-02-segmentation-fault/
- [C++ Segmentation Fault: Causes, Debugging, and Prevention](/en/blog/cpp-error-27-segmentation-fault/
- C++ 스택 vs 힙 | 재귀에서 프로그램이 죽는 이유와 스택 오버플로우 사례
이 글에서 다루는 키워드 (관련 검색어)
C++, Stack overflow, Recursion, Crash, Memory, Debugging 등으로 검색하시면 이 글이 도움이 됩니다.