段错误可精准定位,关键在于用gdb(需-g编译)查看崩溃调用栈和变量值,或用addresssanitizer实时捕获越界/野指针;辅以core dump、编译警告和安全编码实践。

段错误不是随机发生的,它一定对应某个确定的非法内存操作;只要复现稳定,95% 以上的 Segmentation fault 都能准确定位到具体行和变量。
用 GDB 看崩溃时的调用栈和变量值
核心是让程序在崩溃瞬间停下来,而不是直接退出。关键不是“怎么运行”,而是“怎么让它停住并告诉你发生了什么”。
- 编译必须加
-g:否则 GDB 看不到源码和变量名,g++ -g -O0 main.cpp -o main - 启动 GDB 后先
run,等它报出Program received signal SIGSEGV再动手 - 立刻执行
bt(backtrace)——这是第一眼必须看的内容,它会显示从main到崩溃点的完整函数链 - 用
frame 0切到最顶层(崩溃点),再用print ptr或print arr[i]查看可疑变量的实际值 - 如果
bt显示的是??或地址而非函数名,说明没编译调试信息,或用了 strip
用 AddressSanitizer 在运行时自动标出越界/野指针
相比 GDB 的“事后回溯”,AddressSanitizer(ASan)能在非法访问发生的那一行就报错,并明确指出是读越界、写越界还是 use-after-free。
- 编译加
-fsanitize=address -g:例如g++ -fsanitize=address -g main.cpp -o main - 直接运行
./main,崩溃时输出类似:ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001c at pc 0x555555556123 bp 0x7fffffffe0a0 sp 0x7fffffffe098 - 最后一行会精确到文件名、行号、列号,比如:
READ of size 4 at 0x60200000001c thread T0 #0 0x555555556122 in foo() /path/main.cpp:12 - 注意:ASan 会显著拖慢运行速度、增大内存占用,只用于开发和测试阶段,不要上线
检查 core dump 文件确认崩溃上下文
Linux 默认可能禁用 core dump,但一旦开启,它比任何日志都真实——它就是崩溃那一刻整个进程内存的快照。
- 先确认是否生成:运行后检查当前目录是否有
core或core.<pid></pid>;若无,执行ulimit -c unlimited再试 - 用 GDB 加载:
gdb ./main core,然后直接bt,效果和实时调试一致 - 如果程序是多线程的,
info threads和thread apply all bt能看到所有线程状态,常用于排查析构期崩溃(比如 CLI::App 析构时另一线程还在回调) - 注意:core 文件默认不记录环境变量和寄存器全部状态,但对定位空指针、数组越界已足够
别跳过编译器警告和静态检查
很多段错误其实在编译阶段就有迹可循,只是被忽略了。GCC/Clang 的警告不是建议,是潜在未定义行为的提示。
- 至少启用
-Wall -Wextra,重点盯住:‘ptr’ may be used uninitialized、array subscript is above array bounds - 如果用了
std::vector::operator[],它不做边界检查;换成.at()可在运行时报std::out_of_range异常,比段错误更容易捕获 - 对裸指针,强制初始化为
nullptr;释放后立即置为nullptr——这不能防野指针,但能让问题暴露得更早、更一致 - 递归函数务必检查终止条件,尤其当参数来自用户输入时;栈溢出不会报详细位置,
bt里会看到几百层重复的同一函数调用
真正难处理的段错误,往往不是第一次访问就崩,而是 A 处写坏了内存,B 处才触发崩溃;这时候单靠 bt 会误判 B 是根因。必须配合 ASan 或 Valgrind 找到最初的非法写入点——而这个点,常常藏在看似“安全”的指针算术或 memcpy 长度计算里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











