C 指针常见错误
更新时间:2026-08-26。本文是
languages/c/主题入门层第 42 篇,指针专题收官。前九篇的知识点散在各处,这一篇聚成一份指针排错手册:十大错误、每个的现象与定位法,以及一整套"指针崩溃排查流程"。
本文要回答的问题
- 指针程序最常见的十个错误是什么?
- 每个错误的现象怎么和原因对上?
- 拿到一个指针崩溃,排查流程是什么?
一、十大指针错误速查
| # | 错误 | 典型现象 | 定位法 |
|---|---|---|---|
| 1 | 未初始化指针 | 随机崩溃 | -Wall + ASan |
| 2 | NULL 解引用 | 段错误(可控) | gdb bt |
| 3 | free 后使用(悬垂) | 数据错乱 | ASan: use-after-free |
| 4 | 返回局部变量地址 | 随机垃圾值 | 代码审查 |
| 5 | 越界(p + n 超数组) | 段错误/数据错乱 | ASan: buffer-overflow |
| 6 | 类型错配(int* 当 char*) | 解释错乱 | 编译器警告 |
| 7 | 忘了 & | 类型错误/崩溃 | 编译器警告 |
| 8 | 忘了 *(解引用) | 改的是地址 | 代码审查 |
| 9 | 双 free | 崩溃/abort | Valgrind: invalid free |
| 10 | 泄漏(malloc 不 free) | 内存涨 | Valgrind: definitely lost |
二、十个坑的共性与根因
1-5 是"指针指向了不该指的地方"(生命周期/边界问题),6-8 是"用法写错"(语法/类型问题),9-10 是"内存管理问题"。三类各有对策:
| 类别 | 根因 | 防御 |
|---|---|---|
| 指向错误 | 生命周期没管好 | 声明即初始化、free 置 NULL、判空 |
| 用法错误 | 语法没吃透 | -Wall 兜底、多看声明 |
| 内存管理 | 分配/释放不成对 | 谁分配谁释放、Valgrind |
三、排查流程:五步定位法
第 1 步:复现 + 确认。写最小复现(能跑出崩溃的最小代码),一半的 bug 在这步就暴露了。
第 2 步:开 ASan 跑。越界/悬垂/双 free 是 ASan 的拿手菜:
bash
gcc -O0 -g -fsanitize=address -o prog prog.c && ./progASan 会给出精确到行的报告,直接告诉你崩在哪、越界几个字节。
第 3 步:gdb 看调用栈:
bash
gdb ./prog
(gdb) run
(gdb) bt # 崩溃点的调用链
(gdb) info localsbt 告诉你"谁调用谁、崩在哪一层",配合 p(print 变量)看地址和值。
第 4 步:Valgrind 查内存(ASan 没抓到的时候):
bash
valgrind --leak-check=full ./prog第 5 步:代码审查。工具都没抓到,回头看逻辑:谁的生命周期管理、有没有边界算错。-Wall -Wextra -Wpedantic 全开再编译一遍。
四、gdb 里看指针的黄金命令
text
(gdb) p ptr # 打印指针的值(地址)
(gdb) p *ptr # 打印指向的值
(gdb) p &x # 打印变量的地址
(gdb) x/4bx ptr # 看 ptr 指向的内存(4 个字节)
(gdb) p sizeof(ptr) # 指针大小(8)判断"指针是不是 NULL / 是不是垃圾地址",p ptr 一眼看出:0x0 是 NULL,0x7fff... 像栈地址,0x55.../0x4... 通常像堆/代码地址,0x41414141 那种"整块重复"多半是越界写的毒字节。
五、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
只加 -g 不加 -Wall | 类型错误漏网 | 全套警告选项 |
| 编译不更新就 gdb | 行号错位 | 重新编译再调试 |
| 崩溃点 ≠ 错误点 | 定位错方向 | 用 bt 向上找 |
| 复用释放的内存 | 难复现 | ASan/Valgrind 必跑 |
忘了 ulimit -c | 没 core 可查 | 调试期开启 |
六、与本站主线衔接
一句话总结
指针十大错分三类:指向错(生命周期)、用法错(语法)、管理错(分配释放);排查五步:最小复现 → ASan → gdb bt → Valgrind → 代码审查;p ptr 看地址、bt 看调用链——手册在手,指针崩溃不再慌。