第一章: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的工作方式,我画了一张流程图:
1.6 避坑指南
常见错误1:忘记加 -g 选项
如果你编译时没加 -g,GDB虽然能运行,但你看不到源码,只能看到汇编。我曾经有一次调试一个线上bug,发现core dump文件里全是地址,就是因为编译时没加 -g。后来重新编译才定位到问题。
常见错误2:优化选项干扰调试
编译时如果加了 -O2 或 -O3,编译器会优化代码,导致你单步执行时跳来跳去,变量值也可能不对。调试阶段建议用 -O0(默认)或者 -Og(优化但保留调试信息)。
1.7 本章小结
这一章我们走完了GDB的入门流程:
- 知道了GDB是什么——一个让你「钻进程序内部」的调试器
- 了解了它能解决什么问题——崩溃、越界、死锁、链接错误
- 完成了安装和版本确认
- 亲手跑了第一个调试会话
说实话,第一次用GDB时,你可能觉得命令行操作不如IDE方便。但等你真正遇到那些IDE搞不定的疑难杂症时,你就会发现GDB的威力了。
下一章,我们会深入GDB的断点机制,看看如何用条件断点、数据断点来精准定位问题。嗯,到时候你会学到一些真正实用的技巧。