第一章:GDB入门——从零开始认识这个调试利器

1.1 GDB是什么?

GDB,全称GNU Debugger,是Linux生态下最经典的调试器。说白了,它就是一个能让你「钻进程序内部」看个究竟的工具。

我刚开始接触C/C++时,调试全靠printf大法。代码里到处是printf("here 1")、printf("here 2")... 后来被一位老同事看到,他笑着说:「你还在用这种原始方式?试试GDB吧。」

嗯,从那以后,我再也没回头写过调试用的printf。

GDB能做什么?简单来说:

  • 设置断点——让程序在指定行停下来
  • 单步执行——一行一行地看代码怎么跑
  • 查看变量——随时检查内存里的值对不对
  • 分析崩溃——程序挂了,它能告诉你挂在哪里
  • 反汇编——想看汇编级别的执行?没问题

1.2 GDB能解决什么问题?

你想想看,写代码时最怕什么?

  • 程序莫名其妙崩溃,core dump生成了一堆乱码
  • 指针越界,数据被改得面目全非
  • 多线程死锁,程序卡住不动
  • 库链接错误,函数调用到了奇怪的地方

这些场景,GDB都能派上用场。我个人习惯是:只要程序出了诡异的问题,第一反应就是「上GDB看看」。

核心观点:GDB不是万能的,但没有GDB,很多问题你根本无从下手。

1.3 GDB的安装与版本确认

安装GDB其实很简单。大多数Linux发行版都自带,如果没有,一条命令搞定:

# Ubuntu/Debian
sudo apt-get install gdb

# CentOS/RHEL
sudo yum install gdb

# macOS (通过Homebrew)
brew install gdb

安装完成后,确认版本:

$ gdb --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
...

我建议你至少使用GDB 8.0以上版本,新版本对C++17、Python脚本支持更好。我曾经在GDB 7.12上调试一个C++17的lambda表达式,结果各种报错,升级到9.2后一切正常。

1.4 第一个GDB调试会话

光说不练假把式。我们来写一个最简单的程序,然后亲手调试它。

先准备一个测试文件 hello.c:

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;
    int c = a + b;
    printf("c = %d\n", c);
    return 0;
}

编译时一定要加 -g 选项,否则GDB看不到源码信息:

gcc -g -o hello hello.c

然后启动GDB:

$ gdb ./hello

你会看到类似这样的输出:

GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
...
Reading symbols from ./hello...
(gdb) _

看到 (gdb) 提示符,说明已经进入调试模式了。我们来试试几个基本命令:

(gdb) break main          # 在main函数入口设置断点
Breakpoint 1 at 0x1149: file hello.c, line 3.

(gdb) run                  # 运行程序
Starting program: /home/user/hello 
Breakpoint 1, main () at hello.c:3
3           int a = 10;

(gdb) print a              # 查看变量a的值
$1 = 0                     # 还没赋值,所以是0

(gdb) next                 # 执行下一行
4           int b = 20;

(gdb) print a
$2 = 10                    # 现在a被赋值为10了

(gdb) next
5           int c = a + b;

(gdb) print b
$3 = 20

(gdb) next
6           printf("c = %d\n", c);

(gdb) print c
$4 = 30                    # 计算结果正确

(gdb) continue             # 继续执行到结束
c = 30
[Inferior 1 (process 12345) exited normally]

(gdb) quit                 # 退出GDB

小技巧:GDB命令可以简写。比如 break 可以写成 b,next 可以写成 n,print 可以写成 p。调试多了,你会发现这些简写能省不少时间。

1.5 GDB调试的核心流程

为了让你更直观地理解GDB的工作方式,我画了一张流程图:

GDB调试核心流程 编译加 -g 选项 gdb ./程序名 break 函数名/行号 run / continue 常用命令 b main → 设断点 r → 运行 n → 下一步 s → 进入函数 p 变量名 → 打印值 bt → 查看调用栈 c → 继续执行 q → 退出 更多命令后续章节展开

1.6 避坑指南

常见错误1:忘记加 -g 选项

如果你编译时没加 -g,GDB虽然能运行,但你看不到源码,只能看到汇编。我曾经有一次调试一个线上bug,发现core dump文件里全是地址,就是因为编译时没加 -g。后来重新编译才定位到问题。

常见错误2:优化选项干扰调试

编译时如果加了 -O2 或 -O3,编译器会优化代码,导致你单步执行时跳来跳去,变量值也可能不对。调试阶段建议用 -O0(默认)或者 -Og(优化但保留调试信息)。

1.7 本章小结

这一章我们走完了GDB的入门流程:

  • 知道了GDB是什么——一个让你「钻进程序内部」的调试器
  • 了解了它能解决什么问题——崩溃、越界、死锁、链接错误
  • 完成了安装和版本确认
  • 亲手跑了第一个调试会话

说实话,第一次用GDB时,你可能觉得命令行操作不如IDE方便。但等你真正遇到那些IDE搞不定的疑难杂症时,你就会发现GDB的威力了。

下一章,我们会深入GDB的断点机制,看看如何用条件断点、数据断点来精准定位问题。嗯,到时候你会学到一些真正实用的技巧。

可可精品资料库