3. GDB入门:GDB的安装与启动、基本命令

调试器这东西,说白了就是程序员的「后悔药」。你写了个bug,程序跑飞了,core dump了,这时候GDB就是你的救命稻草。我刚开始做C/C++开发那会儿,总觉得printf大法好,直到有一次线上问题,日志根本打不出来——嗯,从那以后我老老实实学起了GDB。

3.1 GDB是什么?为什么需要它?

GDB,全称GNU Debugger,是Linux下最经典的调试工具。它让你能「钻进」程序内部,看变量值、看调用栈、看内存布局。你想想看,没有GDB的时候,你只能靠猜;有了GDB,你就能亲眼看到程序是怎么一步步走到崩溃的。

我个人习惯把GDB比作「程序员的CT机」——它能给运行中的程序做断层扫描。不管是段错误、死循环,还是逻辑错误,GDB都能帮你定位。

核心能力一览:

  • 启动程序并控制执行流程
  • 在指定位置暂停程序(断点)
  • 单步执行,逐行观察代码行为
  • 查看和修改变量、寄存器、内存
  • 分析崩溃时的调用栈(backtrace)

3.2 安装GDB

安装GDB其实很简单。不同发行版命令略有不同,但核心思路一样。

# Ubuntu/Debian
sudo apt-get install gdb

# CentOS/RHEL/Fedora
sudo yum install gdb
# 或者
sudo dnf install gdb

# macOS (通过Homebrew)
brew install gdb

# 验证安装
gdb --version

装完之后,跑一下 gdb --version,看到版本号就说明成功了。我遇到过有人装了半天发现没装对——其实就一条命令的事。

小提示:如果你在macOS上遇到签名问题,记得给GDB做代码签名。具体步骤网上有,这里不展开。Windows用户可以用MinGW或WSL里的GDB。

3.3 启动GDB

启动GDB有三种常见方式。我平时用得最多的是第一种。

# 方式一:直接调试可执行文件
gdb ./my_program

# 方式二:调试core dump文件
gdb ./my_program core

# 方式三:附加到正在运行的进程
gdb -p <PID>

举个例子,假设你有个程序叫 hello,编译时记得加 -g 选项:

gcc -g -o hello hello.c
gdb ./hello

进入GDB后,你会看到 (gdb) 提示符。这时候就可以输入命令了。

注意:编译时一定要加 -g 选项!不加的话,GDB就看不到源码和符号信息,调试体验大打折扣。我曾经帮同事排查问题,发现他编译时没加 -g,结果GDB里全是地址,根本没法看。

3.4 核心命令详解

GDB命令很多,但入门只需要掌握7个。我把它们分成三组来讲。

3.4.1 运行与控制:run, continue

run 命令启动程序。如果程序需要命令行参数,直接跟在后面:

(gdb) run arg1 arg2
(gdb) r  # 简写形式

continue 命令让暂停的程序继续执行,直到遇到下一个断点或程序结束:

(gdb) continue
(gdb) c  # 简写形式

我有个习惯:第一次启动程序时,先用 run 看看能不能正常跑起来。如果直接崩了,那就省事了——直接看backtrace。

3.4.2 断点与单步:break, next, step

break 设置断点。可以按函数名、行号、文件名+行号来设:

(gdb) break main          # 在main函数入口
(gdb) break 42             # 在当前文件的第42行
(gdb) break hello.c:15     # 在hello.c的第15行
(gdb) b main               # 简写形式

nextstep 都是单步执行,但区别很大:

  • next (n):逐过程执行。遇到函数调用,直接执行完,不进入函数内部。
  • step (s):逐语句执行。遇到函数调用,会进入函数内部。

举个例子:

// 假设有这段代码
int result = add(3, 5);   // 第10行
printf("%d\n", result);   // 第11行

// 在第10行停下后:
(gdb) next   # 直接执行完add函数,停在11行
(gdb) step   # 进入add函数内部

什么时候用next,什么时候用step?我的经验是:如果你怀疑某个函数内部有问题,就用step钻进去看;如果只是想跳过它,就用next。说白了,step是「深挖」,next是「跳过」。

3.4.3 查看状态:print, backtrace

print 命令查看变量值。这是最常用的命令之一:

(gdb) print x              # 查看变量x的值
(gdb) p x                  # 简写
(gdb) p &x                 # 查看x的地址
(gdb) p *ptr               # 查看指针指向的值
(gdb) p array[3]           # 查看数组元素
(gdb) p func()             # 甚至可以调用函数!

backtrace 查看函数调用栈。程序崩溃时,这是第一个要用的命令:

(gdb) backtrace
(gdb) bt                   # 简写
(gdb) bt full              # 显示每个栈帧的局部变量

输出大概长这样:

#0  0x00005555555546a7 in divide (a=10, b=0) at calc.c:8
#1  0x00005555555546d4 in main () at calc.c:14

看到 b=0 了吗?这就是除零错误的根源。我曾经帮一个实习生排查段错误,他盯着代码看了半小时没找到问题。我让他跑一下 bt,三秒钟就定位到了——空指针解引用。

调试黄金流程:

  1. 程序崩溃 → 先跑 bt 看调用栈
  2. 找到崩溃位置 → 用 list 看附近源码
  3. print 查看相关变量值
  4. 分析变量为什么不对 → 设置断点重新跑
  5. next/step 跟踪变量变化

3.5 实战演练:调试一个除零错误

光说不练假把式。我们拿一个简单的例子走一遍流程。

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

int divide(int a, int b) {
    return a / b;  // 第4行
}

int main() {
    int x = 10;
    int y = 0;
    int z = divide(x, y);  // 第9行
    printf("Result: %d\n", z);
    return 0;
}

编译并启动GDB:

gcc -g -o calc calc.c
gdb ./calc

调试过程:

(gdb) run
Starting program: /tmp/calc
Program received signal SIGFPE, Arithmetic exception.
0x00005555555546a7 in divide (a=10, b=0) at calc.c:4
4           return a / b;

(gdb) bt
#0  0x00005555555546a7 in divide (a=10, b=0) at calc.c:4
#1  0x00005555555546d4 in main () at calc.c:9

(gdb) print b
$1 = 0

(gdb) print a
$2 = 10

(gdb) list
1       #include <stdio.h>
2
3       int divide(int a, int b) {
4           return a / b;
5       }
6
7       int main() {
8           int x = 10;
9           int y = 0;
10          int z = divide(x, y);

看到了吧?b=0,除零错误。修复方法就是在 divide 函数里加个判零检查。

我的习惯:调试时我会在 main 函数入口设个断点,然后一步步走。这样能完整看到程序从启动到崩溃的全过程。有时候bug不在崩溃点,而在某个变量被错误修改的地方。

3.6 常用命令速查表

命令 简写 作用
run r 启动程序
break b 设置断点
next n 单步执行(跳过函数)
step s 单步执行(进入函数)
continue c 继续执行
print p 打印变量值
backtrace bt 查看调用栈
list l 显示源码
quit q 退出GDB

3.7 知识体系图

下面这张图帮你理清GDB入门阶段的核心脉络:

GDB入门知识体系 GDB调试器 安装与启动 核心命令 实战调试流程 apt-get / yum 安装 gdb ./program gdb -p <PID> run / continue break / next / step print / backtrace bt 定位崩溃点 print 查看变量 断点+单步跟踪 掌握这7个命令,就能解决80%的调试问题

嗯,这张图把GDB入门的核心脉络梳理清楚了。你从安装开始,掌握7个核心命令,然后按照实战流程走一遍,基本就能上手了。记住:调试不是玄学,是有方法论的。

避坑指南:我曾经在调试多线程程序时,用 next 跳过一个线程创建函数,结果程序直接跑飞了。后来才意识到——多线程调试要用 thread 命令切换线程上下文。这个后面章节会细讲,但你现在要知道:GDB默认只跟踪主线程。

好了,GDB入门就讲到这里。这些命令看着简单,但用熟了之后,调试效率能翻好几倍。下一节我们聊GDB的高级断点技巧——条件断点、硬件断点、数据断点,这些才是真正拉开差距的东西。


公众号:蓝海资料掘金营,微信deep3321