1. GDB入门
1.1 GDB是什么?
GDB,全称GNU Debugger,是Linux平台上最经典的调试器。没有之一。
说白了,GDB就是一个能让你「钻进程序内部」的工具。你写的C++代码编译成二进制后,程序跑起来就像个黑盒子——你不知道它内部在干什么。GDB就是那个让你打开黑盒子的钥匙。
我个人习惯把GDB比作「程序的CT机」。它能扫描程序的每一行代码、每一个变量、每一块内存。程序哪里出了问题,一照便知。
对于Qt开发者来说,GDB还有一个特殊的意义:它能帮你看到信号槽的调用链。这一点,后面我会详细讲。
1.2 GDB能做什么?
GDB能做的事情,比你想象的多得多。我列几个最常用的场景:
- 设置断点:让程序在指定行停下来,观察变量状态
- 单步执行:一行一行地跑代码,看程序是怎么走的
- 查看变量:随时查看任意变量的值,包括指针指向的内容
- 查看调用栈:程序崩溃时,看看到底是哪一层函数出了问题
- 修改内存:运行时直接改变量的值,测试不同情况
- 调试多线程:切换线程、查看线程状态
- 调试Qt信号槽:追踪信号发射和槽函数调用的全过程
嗯,这里要注意:GDB不是IDE里的那个「调试按钮」。它是命令行工具,但功能比大多数IDE的调试器都要强大。
核心观点:GDB是C++程序员的最后一道防线。当你的程序在IDE里跑得好好的,一上线就崩,GDB就是你最可靠的伙伴。
1.3 GDB的安装与启动
安装GDB非常简单。不同系统命令略有不同:
| 系统 | 安装命令 |
|---|---|
| Ubuntu/Debian | sudo apt install gdb |
| CentOS/RHEL | sudo yum install gdb |
| macOS | brew install gdb |
| Windows (MSYS2) | pacman -S mingw-w64-x86_64-gdb |
安装完成后,验证一下:
gdb --version
看到版本号就说明装好了。
启动GDB有两种方式:
# 方式一:直接启动,再加载程序
gdb
(gdb) file ./myapp
# 方式二:启动时直接加载程序(我常用这个)
gdb ./myapp
启动后你会看到 (gdb) 提示符,说明已经进入GDB交互模式了。
小技巧:编译时记得加 -g 选项,否则GDB看不到源码和变量名。我见过太多人忘了加这个,然后说GDB不好用……
1.4 GDB的基本命令概览
GDB的命令很多,但常用的就那么几个。我按使用频率给你列一下:
启动与退出
run # 运行程序(可以带参数)
quit # 退出GDB
help # 查看帮助
断点相关
break main.c:42 # 在main.cpp第42行设断点
break functionName # 在函数入口设断点
info breakpoints # 查看所有断点
delete 1 # 删除编号为1的断点
执行控制
next # 单步执行(不进入函数)
step # 单步执行(进入函数)
continue # 继续执行到下一个断点
finish # 执行完当前函数
查看数据
print varName # 打印变量值
display varName # 每次停下时自动打印变量值
backtrace # 查看调用栈
list # 查看当前附近的源码
这些命令看起来不多,但组合起来威力巨大。你想想看,一个 break 加一个 print,就能解决80%的bug。
避坑指南:我曾经在调试一个Qt程序时,发现信号槽连接后槽函数没被调用。用GDB在 emit 语句处设断点,再用 step 进入信号发射过程,才发现是连接时参数类型不匹配。如果没有GDB,这种问题排查起来非常痛苦。
1.5 GDB调试流程:一张图看懂
下面这张图展示了GDB调试的核心流程。我把它画出来,方便你理解整个调试过程是怎么串起来的。
这张图的核心逻辑很简单:编译 → 启动GDB → 设断点 → 运行 → 调试循环。其中「调试循环」是最关键的部分,你会在 next、step、print、continue 这几个命令之间反复切换,直到找到问题根源。
1.6 一个简单的调试示例
光说不练假把式。我们来看一个实际例子:
// main.cpp
#include <iostream>
int main() {
int a = 10;
int b = 0;
int c = a / b; // 这里会崩溃
std::cout << c << std::endl;
return 0;
}
编译并调试:
g++ -g main.cpp -o test
gdb ./test
(gdb) run
Program received signal SIGFPE, Arithmetic exception.
0x000055555555515e in main () at main.cpp:5
5 int c = a / b;
(gdb) print a
$1 = 10
(gdb) print b
$2 = 0
(gdb) quit
看到了吗?程序崩溃后,GDB直接告诉你:第5行,除零错误。然后你 print b 一看,b是0。问题一目了然。
这就是GDB最基础、最核心的用法。你不需要记住所有命令,先掌握 run、break、next、print、continue 这五个,就能解决大部分问题了。
我的建议:刚开始用GDB时,别急着学高级功能。先把这五个命令练熟,遇到问题能快速定位变量值和崩溃位置,你就已经超过80%的开发者了。