段错误(Segmentation fault)怎么排查?
更新时间:2026-09-20。问题驱动短答页。
Segmentation fault (core dumped)本质是进程访问了无效或无权限的虚拟地址,MMU 触发缺页/保护异常,内核发SIGSEGV终止进程。机制见空指针解引用与段错误。
一句话回答
先用 gdb 直接跑、崩溃处 bt 看调用栈定位到具体行;难以复现就打开 core dump 事后 gdb ./app core;最快的是编译加 -fsanitize=address 用 AddressSanitizer,它会直接打印越界/空指针/use-after-free 的精确位置。
常见成因(按出现频率)
| 成因 | 典型场景 |
|---|---|
| 空指针解引用 | malloc 返回没判空、函数返回 NULL 直接 -> |
| 野指针/悬垂指针 | 未初始化指针、free 后继续用(use-after-free) |
| 数组/缓冲区越界 | 下标超界、字符串没 \0、strcpy 溢出 |
| 栈溢出 | 无限/过深递归、栈上开超大数组 |
| 写只读内存 | 改写字符串字面量、const 数据 |
| 函数指针非法 | 未初始化/被踩坏的函数指针调用 |
| 并发踩内存 | 数据竞争把指针写坏(现象飘忽) |
三板斧
① gdb 直接跑,崩溃看栈
bash
gcc -g -O0 main.c -o main
gdb ./main
(gdb) run
# 崩溃后:
(gdb) bt # 调用栈,定位到你的源码行
(gdb) frame 2 # 进入相关栈帧
(gdb) print p # 看指针是不是 0x0、变量是否异常② core dump 事后分析
bash
ulimit -c unlimited
./main # 崩溃生成 core
gdb ./main core # 或 gdb ./main /path/core.xxx
(gdb) btcore 不生成时查 ulimit -c(为 0)与 cat /proc/sys/kernel/core_pattern(可能被转给 abrtd/apport 或带管道)。详见 core dump 怎么生成分析。
③ AddressSanitizer(强烈推荐)
bash
gcc -g -fsanitize=address -fno-omit-frame-pointer main.c -o main_asan
./main_asanASan 会在越界、UAF、重复 free 的第一现场中止,并打印分配栈/释放栈/访问栈,比 gdb 猜半天快得多。C 端工具见 ASan 内存检测、Valgrind。
按现象分流
- 稳定崩溃、地址接近 0x0:空指针,检查返回值/初始化。
p是看着合法但怪异的地址、时好时坏:野指针/UAF/被踩内存,上 ASan 或 Valgrind。- 递归函数里崩:栈溢出,改迭代或
ulimit -s/调线程栈。 - 多线程下偶发:数据竞争写坏指针,用 TSan(
-fsanitize=thread)或加锁审查。 - free 附近出问题:重复释放/越界写破坏了堆块头,ASan 最直接。
预防
- 指针定义即初始化、malloc 后必判空。
free(p)后置p=NULL,避免悬垂。- 字符串操作用带长度的
snprintf/strncpy,注意结尾\0。 - CI 里常态化跑 ASan/Valgrind,编译开
-Wall -Wextra。 - 大数组放堆上、深递归改迭代。
总结
段错误 = 访问未映射或无权限内存被 SIGSEGV 终止,多为空/野指针、越界、use-after-free、栈溢出或写只读区;定位三板斧是 gdb 运行后在崩溃点 bt 看栈、开 core dump 做事后 gdb 分析、以及用 -fsanitize=address 让 ASan 在越界/UAF 第一现场打印精确调用栈;地址近 0 多为空指针、现象飘忽多为悬垂/竞态、递归中崩为栈溢出,配合判空、free 置空和常态化 ASan 可在源头预防。