调用栈深度疯狂增长且backtrace中同一函数反复出现,基本是死递归或递归过深;需优先检查终止条件是否触发、局部大数组是否导致栈溢出,并结合栈地址变化、线程栈限制及调用链长度交叉验证。

看调用栈深度是不是在疯狂增长
程序一运行就崩,GDB 或 VS 的调试器里看到 backtrace 里全是同一个函数反复出现(比如 parse_node、dfs、甚至 main 自己调自己),基本就是死递归或递归过深。这时候别急着改逻辑,先确认终止条件有没有被真正触发——常见坑是 if (n 写成 <code>if (n = 0),赋值操作永远返回 true;或者递归参数没变小(比如忘了 n-1 写成 n)。
临时验证方法:在递归入口加个计数器,static int depth = 0; depth++; if (depth > 1000) { abort(); }。如果真卡在 1000 层以内就 abort,说明不是深度问题,得往别处查。
检查函数里有没有声明超大局部数组
Windows 默认栈只有 1MB,Linux 一般是 8MB,但一个 int arr[200000] 就占 800KB,再套两层函数调用,很容易撞线。这种问题往往“注释掉某行就正常”,特别隐蔽。
- 用
sizeof(arr)算下实际字节数,超过几百 KB 就该警惕 - 把大数组改成
std::vector<int> arr(200000)</int>或new int[200000],内存就挪到堆上了 - VS 里可通过项目属性 → 配置属性 → 链接器 → 系统 → 堆栈保留大小,临时调大(比如设成
16777216即 16MB),仅用于定位,不能当长期解法
用 ulimit 或 _stacksize 偷看当前栈限制
Linux 下跑 ulimit -s 能看到软限制(单位 KB),比如输出 8192 就是 8MB;Windows 下没法直接查,但 VS 调试时崩溃弹窗里常带 “Stack overflow” 和模块名,结合 PDB 符号能快速锁定出问题的函数。
立即学习“C++免费学习笔记(深入)”;
更准的办法是在可疑函数开头加一句:char dummy; printf("stack addr: %p\n", &dummy);,多打几次,看地址是不是一路往下掉(栈向下生长),掉得太猛就说明快到底了。
线程栈不够用时容易被忽略
主线程栈够用,但你自己 std::thread 创建的子线程,默认栈可能只有 1MB(POSIX 线程默认值),而主线程是进程级设置的。这时候即使代码完全一样,子线程一进递归或分配大数组就崩。
解决方式:
- Linux:创建线程前用
pthread_attr_setstacksize(&attr, 8 * 1024 * 1024)手动设大点 - C++11:不用裸
pthread,改用std::thread时,把大对象传进去而不是在新线程函数里声明 - 更稳妥:彻底避免在线程函数里分配 >64KB 的栈变量
栈溢出最麻烦的地方不是它难修,而是它不报具体哪一行——错误发生在栈“撑爆”的瞬间,而压垮骆驼的最后一根稻草,往往只是某个看似无害的 std::string 临时对象或隐式拷贝。所以排查时别只盯“明显的大数组”或“明显的递归”,要从调用链长度、栈地址变化、线程上下文三处交叉验证。


















