1. GDB入门:从零开始认识调试器

大家好,我是你们的老朋友。今天咱们聊聊GDB——这个我用了十几年、几乎每天都在用的调试工具。

说实话,刚入行那会儿,我总觉得调试就是printf大法。直到有一次,一个内存越界的bug让我整整熬了三个通宵...嗯,从那以后,我才真正开始认真学GDB。

1.1 GDB是什么?

GDB,全称GNU Debugger,是GNU项目下的一个调试器。说白了,它就是一个能让你「钻进程序内部」的工具。

你想想看,平时我们写代码,程序跑起来就像个黑盒子。你只知道输入和输出,中间发生了什么?不知道。GDB就是帮你打开这个黑盒子的钥匙。

核心能力:

  • 让程序在任意位置暂停执行
  • 查看变量、内存、寄存器的值
  • 单步执行,逐行观察代码行为
  • 修改程序状态,测试不同场景

1.2 GDB能解决什么问题?

我在项目中遇到过太多场景,没有GDB根本搞不定。这里列几个典型的:

问题类型 传统做法 GDB做法
程序崩溃(段错误) 加printf猜位置 直接定位到崩溃行,看调用栈
变量值不对 打印日志 断点停下,实时查看变量
死循环 Ctrl+C重启 暂停后看当前执行位置
内存泄漏 靠经验猜 配合工具查看堆内存
逻辑分支走错 加条件打印 条件断点,精准命中

我曾经接手过一个项目,程序跑着跑着就崩,没有任何日志。团队里有人说「加printf试试」,我说「别,咱们用GDB」。结果第一次attach上去,不到10分钟就找到了问题——一个野指针在作怪。

1.3 GDB的安装与启动

安装GDB其实很简单。不同平台略有差异:

Linux(Ubuntu/Debian)

sudo apt-get install gdb

macOS

brew install gdb
# 或者用Xcode自带的lldb(GDB的变种)

Windows

我建议用MinGW或者WSL。Windows原生支持不太好,但也能用。

# MinGW环境下
pacman -S mingw-w64-x86_64-gdb

小提示:安装完后,在终端输入 gdb --version 检查是否成功。我习惯用 gdb -q 启动,这样不会显示版权信息,清爽很多。

1.4 GDB的基本工作流程

GDB的工作流程,说白了就三步:启动、设断点、调试。我画了一张图,帮你快速理解:

GDB基本工作流程 1. 启动GDB gdb ./myprogram 2. 设置断点 break main 3. 运行程序 run 4. 调试操作 next(下一步) | step(进入函数) | print(查看变量) | continue(继续运行) 5. 退出:quit

这张图展示了GDB的核心流程。我来详细说说每一步:

第一步:启动GDB

gdb ./myprogram

这会加载你的可执行文件。注意,编译时一定要加 -g 选项,否则GDB看不到源码和变量名。

警告:编译时忘记加 -g?那你看到的只有一堆汇编代码,根本没法调试。我曾经在客户现场犯过这个错,尴尬得不行。

第二步:设置断点

(gdb) break main
(gdb) break 42          # 在第42行设断点
(gdb) break func_name   # 在函数入口设断点

断点就是让程序停下来的「路障」。你可以设多个,也可以设条件断点。

第三步:运行程序

(gdb) run
(gdb) run arg1 arg2     # 带参数运行

程序会在第一个断点处停下。这时候,你就可以开始「审讯」它了。

第四步:调试操作

这是最常用的几个命令:

  • next(或 n):执行下一行,不进入函数内部
  • step(或 s):执行下一行,进入函数内部
  • print var(或 p var):查看变量值
  • backtrace(或 bt):查看调用栈
  • continue(或 c):继续运行到下一个断点

我的习惯:调试时我一般先用 next 快速过一遍,发现可疑的地方再用 step 钻进去看。别一上来就step,容易把自己绕晕。

第五步:退出

(gdb) quit

或者直接按 Ctrl+D。

1.5 一个完整的调试示例

光说不练假把式。咱们来看一个实际例子:

// test.c
#include <stdio.h>

int divide(int a, int b) {
    return a / b;  // 如果b=0,这里会崩溃
}

int main() {
    int x = 10;
    int y = 0;
    int result = divide(x, y);
    printf("result = %d\n", result);
    return 0;
}

编译并调试:

$ gcc -g -o test test.c
$ gdb ./test
(gdb) break main
(gdb) run
(gdb) next          # 执行到 int y = 0;
(gdb) print y       # 查看y的值
$1 = 0
(gdb) next          # 执行到 divide(x, y);
(gdb) step          # 进入divide函数
(gdb) print b       # 查看b的值
$2 = 0
(gdb) quit

看到了吗?在进入 divide 之前,我们就已经知道 b 是0了。这时候你可以选择修改 b 的值:

(gdb) set var b = 2
(gdb) continue

程序就能正常跑完了。这就是GDB的威力——你可以在运行时修改程序状态,测试各种场景。

核心要点回顾:

  • GDB是调试利器,能解决段错误、变量异常、逻辑错误等问题
  • 编译时加 -g 选项是前提
  • 工作流程:启动 → 设断点 → 运行 → 调试 → 退出
  • 常用命令:break、run、next、step、print、continue、quit

好了,第一章就到这里。GDB入门其实不难,难的是养成用GDB的习惯。我见过太多工程师,明明装了GDB,遇到bug还是下意识地加printf。别这样,相信我,GDB用熟了,效率能翻倍。

可可精品资料库