1、GDB入门:GDB简介、GDB安装与启动、GDB基本命令概览
1.1 GDB是什么?为什么我们需要它?
说实话,我刚入行那会儿,调试代码全靠printf大法。到处加打印,编译,运行,看输出,改代码,再编译……循环往复。直到有一次,一个野指针问题让我加了整整两天的打印,愣是没定位到。后来一位老同事走过来,敲了几个命令,十分钟就找到了问题根源。那个工具,就是GDB。
GDB,全称GNU Project Debugger,是Linux/Unix环境下最强大的调试工具,没有之一。它让你能「看」进程序的内部——看变量值、看内存、看寄存器、看调用栈。说白了,它给了你一双透视眼。
你想想看,一个程序跑起来就是个黑盒子。你只知道输入和输出,中间发生了什么?哪个变量被改错了?哪条分支走歪了?GDB能让你在任意时刻「暂停」程序,然后像翻书一样,一页一页地检查内部状态。
核心价值一句话:GDB让你从「猜问题」变成「看问题」。
1.2 GDB的安装与启动
安装GDB其实很简单。我习惯用包管理器一把梭,省心。
1.2.1 安装GDB
不同的系统,命令略有不同:
| 系统 | 安装命令 |
|---|---|
| Ubuntu/Debian | sudo apt-get install gdb |
| CentOS/RHEL | sudo yum install gdb |
| macOS (Homebrew) | brew install gdb |
装完之后,在终端敲 gdb --version,能看到版本号就说明搞定了。嗯,这里要注意,macOS上可能需要额外签名,否则调试时会报权限错误。我曾经在这上面卡了半小时,后来发现是系统安全策略的问题。
1.2.2 启动GDB
启动GDB有两种常见姿势:
- 直接调试可执行文件:
gdb ./my_program - 先启动,再加载文件:进入GDB后,敲
file ./my_program
我个人更推荐第一种,一步到位。但如果你已经进了GDB,用第二种也完全OK。
还有一个关键点:编译时一定要加 -g 选项。不加的话,GDB就看不到源码和变量名,只能看到一堆地址。我见过太多新手跑来问「为什么GDB看不到变量」,结果一看,编译命令里没有 -g。
警告:编译时务必加上 -g 选项!例如:gcc -g -o my_program my_program.c。否则调试信息缺失,GDB功能大打折扣。
1.3 GDB基本命令概览
GDB的命令很多,但常用的其实就那么十几个。别被吓到,咱们先掌握最核心的。
1.3.1 启动与退出
gdb ./program—— 启动并加载程序quit或q—— 退出GDBhelp—— 查看帮助。记不住命令?敲help就对了
1.3.2 运行与控制
run或r—— 运行程序start—— 启动程序并在main函数入口暂停continue或c—— 继续运行,直到下一个断点next或n—— 单步执行,不进入函数内部step或s—— 单步执行,会进入函数内部finish—— 运行到当前函数返回
这里有个小技巧:next 和 step 的区别,说白了就是「要不要钻进函数里看看」。如果你确定某个函数没问题,就用 next 跳过;如果你怀疑函数内部有鬼,就用 step 钻进去。
1.3.3 断点管理
break main或b main—— 在main函数入口设断点break 42—— 在第42行设断点break file.c:100—— 在file.c的第100行设断点info breakpoints—— 查看所有断点delete 1—— 删除编号为1的断点disable 1—— 禁用编号为1的断点(不删除)
断点这东西,用好了就是神器。我曾经调试一个多线程死锁问题,就是在关键锁操作前后各设一个断点,然后观察线程执行顺序,才找到问题的。
1.3.4 变量查看与修改
print var或p var—— 打印变量var的值print &var—— 打印变量var的地址display var—— 每次暂停时自动显示var的值set var = 42—— 将变量var的值修改为42whatis var—— 查看变量var的类型
举个例子,假设你怀疑某个循环变量越界了:
(gdb) break 25
(gdb) run
(gdb) print i
$1 = 0
(gdb) continue
(gdb) print i
$2 = 1
(gdb) set i = 100
(gdb) continue
看到了吗?你可以在运行时直接修改变量值,强制让程序走某条分支。这个能力在测试边界条件时特别有用。
1.3.5 查看调用栈
backtrace或bt—— 查看函数调用栈frame N—— 切换到第N层栈帧info locals—— 查看当前栈帧的局部变量
程序崩溃时,第一件事就是敲 bt。它会告诉你「我是谁?我在哪?谁叫我来的?」。我调试段错误时,90%的情况靠 bt 就能定位到问题函数。
1.4 知识体系一览
为了让你对GDB有个整体认识,我画了张图。这张图把GDB的核心功能分成了四大块:启动与配置、运行控制、断点与观察、数据查看与修改。每一块都是调试中不可或缺的。
1.5 一个完整的调试小例子
光说不练假把式。咱们拿一个最简单的程序走一遍流程。
假设有这么一个bug程序 bug.c:
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
printf("Sum = %d\n", sum);
return 0;
}
编译并启动调试:
$ gcc -g -o bug bug.c
$ gdb ./bug
进入GDB后,我们来走一遍:
(gdb) break main # 在main函数设断点
(gdb) run # 运行,停在main入口
(gdb) next # 执行 int sum = 0;
(gdb) print sum # 查看sum的值 → 0
(gdb) next # 进入循环
(gdb) print i # 查看i的值 → 0
(gdb) display sum # 让GDB每次暂停都自动显示sum
(gdb) next # 执行 sum += i;
(gdb) next # i++,然后判断循环条件
(gdb) print i # 查看i → 1
(gdb) continue # 直接跑到结束
看到了吗?每一步你都能看到变量怎么变的。这就是GDB的威力。
小技巧:如果觉得每次敲 next 太累,可以试试直接敲回车——GDB会重复执行上一条命令。这个习惯我用了十年,省了不少键盘寿命。
1.6 避坑指南
最后,分享几个我踩过的坑:
- 忘了加
-g:这是最最常见的错误。没有调试信息,GDB就像瞎子。 - 优化级别太高:用
-O2或-O3编译后,变量可能被优化掉,你根本看不到。调试时建议用-O0。 - strip过的程序:生产环境里的程序经常被strip过,符号表没了。这时候GDB只能看到地址,看不到函数名和变量名。
- 权限问题:调试某些系统级程序时,可能需要root权限。记得加
sudo。
嗯,这一章的内容就到这里。GDB入门其实不难,关键是动手练。找个简单的程序,把上面这些命令挨个敲一遍,比看十遍文档都管用。