C 空指针与段错误
更新时间:2026-08-26。本文是
languages/c/主题入门层第 38 篇。Segmentation fault(段错误)是 C 程序员最常撞见的崩溃——它不打招呼,进程直接死。理解它的机制(内存保护),你就知道为什么"崩溃"其实是系统在保护你。
本文要回答的问题
- 段错误到底是什么"段"错了?
- 为什么空指针解引用必段错误?
- 拿到一个段错误,怎么快速定位?
一、段错误 = 访问了无权访问的内存
现代 CPU + 操作系统有内存保护:每个进程的虚拟地址空间被分成很多页,每页有权限(可读/可写/可执行)。程序访问了不属于自己的地址或权限不符的地址,CPU 抛异常 → 内核杀进程 → 打印 Segmentation fault。
c
int *p = NULL;
*p = 42; // 地址 0 处没有映射页 → 段错误常见触发:
| 场景 | 为什么崩 |
|---|---|
| 空指针解引用 | 地址 0 无权限 |
| 越界数组访问 | 走出数组所在页 |
| 改只读数据 | 权限只读 |
| 栈溢出(递归过深) | 栈耗尽,撞到保护页 |
| 释放后使用 | 页已归还,权限没了 |
记住:段错误不是"程序坏了所以崩",而是"程序想访问某块不允许的内存,被系统拦下了"。崩溃点 = 访问点,是定位的重要线索。
二、空指针解引用:最有名的一种
NULL 就是 0,而地址 0 所在的页对任何进程都没有映射(或者干脆映射为不可访问)——这是内核故意设置的"陷阱区",目的就是让空指针解引用立刻、可检测地崩溃,而不是悄悄破坏数据。
所以:
c
if (p != NULL) { ... } // 好的:先检查再解引用
*p; // 坏的:崩溃点在这崩溃虽然烦,但比"静默写坏内存"强一万倍——它给了你明确的错误位置。
三、定位段错误:三板斧
第一板斧:gdb 看现场(-g 编译是前提):
bash
gcc -O0 -g -o prog prog.c
gdb ./prog
(gdb) run
# 崩溃后:
(gdb) bt # backtrace:函数调用栈,看崩在哪层
(gdb) info locals # 看各变量值
(gdb) list # 看崩溃行bt 是最有价值的一行——它告诉你从 main 到崩溃点经过了哪些函数。
第二板斧:core dump 事后分析:
bash
ulimit -c unlimited # 允许生成 core 文件
./prog # 崩溃,生成 core
gdb ./prog core # 事后开棺验尸第三板斧:ASan 提前拦截:
bash
gcc -O0 -g -fsanitize=address -o prog prog.c
./prog # 越界/悬垂会被更早更准地报告四、一个完整例子:亲手定位
c
#include <stdio.h>
void process(int *p) {
*p = *p + 1; // 如果 p 是 NULL,这里崩
}
int main(void) {
int *data = NULL; // 忘记赋值
process(data); // 崩
return 0;
}gdb 里 bt 输出:
text
#0 process (p=0x0) at segv.c:4
#1 main () at segv.c:10一眼看出:process 的第 4 行解引用了 NULL。修复:调用前判空 + 初始化 data。
五、段错误的"亲戚们"
| 崩溃信息 | 含义 |
|---|---|
Segmentation fault | 访问无权访问的内存 |
Bus error | 非对齐访问等硬件级问题 |
Stack overflow | 栈耗尽(递归过深最常见) |
Illegal instruction | 执行了非法指令(如跳转到数据区) |
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 忘初始化指针 | 段错误 | 声明即初始化 |
| 忘判空 | 段错误 | 解引用前检查 |
编译没加 -g | gdb 看不到行号 | 永远 -g |
| 递归过深 | stack overflow | 改迭代 |
| 生产环境段错误 | 只留个 core | 配好 core 收集 |
七、与本站主线衔接
- 虚拟内存与页权限的机制,见 L5 ELF 与装载;
- 段错误是 crash 排查线的主线,core dump 分析见 crash/core;
- 空指针的兄弟姐妹(野指针),见上一篇 NULL 与野指针。
一句话总结
段错误 = 程序访问了无权限的内存页,被系统拦截——崩溃点是定位线索;空指针解引用必崩是因为地址 0 是"陷阱区";三板斧定位:gdb bt 看调用栈、core dump 事后查、ASan 提前拦;先判空再解引用,是第一道防线。