CC++ & Algorithm

GDB调试器:你的C++程序“侦探”

较难4
语言版本:C++
概述:使用GDB逐步检查程序的执行过程,像侦探一样找出变量和逻辑的错误,让程序听话。

GDB调试器:你的C++程序“侦探”

当你写了一个C++程序,但运行结果和预想不一样时,该怎么办?GDB就是一位聪明的“侦探”,能帮你一步一步地查看程序里发生了什么,找到“凶手”(bug)。它就像你随身携带的慢镜头回放器,让程序的每一步都清晰可见。

?️ 安装和准备:给侦探配好工具包

大多数Linux系统已经自带GDB(可以用gdb --version检查)。如果没有,在终端输入sudo apt-get install gdb(Ubuntu/Debian)即可安装。

要让GDB正常工作,编译时必须带上**-g**选项,这相当于给程序“录下指纹”——把源代码的行号、变量名等信息嵌入到可执行文件中。没有这个选项,GDB就像瞎子一样,只能看到机器码,没法帮你找bug。

g++ -g myprogram.cpp -o myprogram

新手容易犯的错误:调试时忘记加-g,导致run后看不到源代码,只能用stepi等底层指令,非常痛苦。记住:调试版必须加-g,发布版可以去掉以减小体积。

? 启动侦探模式

在终端输入gdb myprogram,就进入了GDB的交互界面,看到(gdb)提示符。这就像侦探坐在桌前,等着你发号施令。按Ctrl+D或输入quit可以退出。

? 常用的侦探指令(必须记住)

下面每个指令都配有生活例子和代码演示,方便你记忆。

1️⃣ 设置暂停点(断点)—— breakb

断点就像你在跑步时放了一个栏杆,程序跑到那里就会停下来等你检查。你可以按行号函数名设置。

(gdb) break 10          # 在第10行停下来
(gdb) break main        # 在main函数的第一行停下来
(gdb) break computeSum  # 在computeSum函数的第一行停下来

生活中的例子:你记录零花钱支出,想知道每次买零食后余额是多少。你就可以在“余额更新”的那一行设置断点,每次停下来查看balance的值。

2️⃣ 开始跑程序 —— runr

程序从第一行启动,跑到第一个断点就暂停。如果程序需要命令行参数,可以这样:run arg1 arg2

(gdb) run   # 不带参数运行
(gdb) run 5 10  # 给程序传递两个参数

3️⃣ 查看源代码 —— listl

list显示当前暂停位置附近的源代码,让你知道自己在哪。

(gdb) list      # 显示当前位置前后10行
(gdb) list 1,20 # 显示第1到第20行

4️⃣ 一步一步走:stepnext

这两个指令是调试的核心,区别在于是否进入函数内部

  • step(或s:像跟踪小偷进入小巷——如果当前行调用了函数,会进入函数内部,一行一行执行。
  • next(或n:像大步跨过一条沟——把整行(包括函数调用)当作一步执行完,不进入函数内部。

新手容易犯的错误:在不需要看函数内部时使用了step,结果一头扎进库函数(如coutvector的底层代码),半天出不来。遇到这种情况,可以用finish跳出当前函数,或者until 行号跳到指定行。

(gdb) step    # 进入当前行调用的函数
(gdb) next    # 不进入函数,直接执行完当前行
(gdb) finish  # 执行完当前函数并返回,停在调用处

5️⃣ 查看变量值 —— printp

这是最频繁使用的命令,让你看到变量当前的“真面目”。

(gdb) print i        # 显示i的值
(gdb) print sum      # 显示sum的值
(gdb) print &i       # 显示i的地址(像侦探查指纹)
(gdb) print arr[3]   # 显示数组arr的第4个元素
(gdb) print *ptr     # 显示指针ptr指向的值

生活中的例子:你写了一个考勤程序,统计小明本月迟到次数。调试时可以用print lateCount查看当前次数,检查是不是每次加1。

另外,display命令可以设置自动显示,每次暂停时自动输出某些变量的值,省去反复输入print的麻烦。

(gdb) display i      # 每次暂停都自动显示i

6️⃣ 继续跑完 —— continuec

程序从暂停处继续执行,直到下一个断点或程序结束。

(gdb) continue

7️⃣ 退出侦探 —— quitq

(gdb) quit

? 额外有用的指令

  • watch 变量名:当变量值发生变化时自动暂停(像设了一个“变化报警器”)。
    比如watch sum,每次sum被修改就会暂停,非常适合追踪变量何时被意外改变。

  • backtrace(或bt:显示当前函数调用堆栈,就像看一个电话簿,知道当前是谁调用了谁。
    当程序崩溃时,backtrace能告诉你崩溃发生在哪一层。

  • info locals:显示当前作用域内所有局部变量的值,快速浏览。

? 完整示例:找出累加错误并体验调试过程

假设你想计算1加到10,但结果却不对劲(比如总是多出一些)。原始代码故意埋了一个错误:

// sum.cpp
#include <iostream>
using namespace std;

int main() {
    int sum = 0;           // 存累加结果
    for (int i = 1; i <= 10; i++) {
        sum += i + 1;      // 故意写错:本该是 sum += i
    }
    cout << "和是: " << sum << endl;
    return 0;
}

调试过程(在终端逐行输入):

g++ -g sum.cpp -o sum      # 带调试信息编译
gdb sum                     # 启动侦探
(gdb) break main           # 在main函数开头设断点
(gdb) run                  # 运行,停在main第一行
(gdb) next                 # 执行到for循环的sum+=i+1那一行
(gdb) print i              # $1 = 1,i是1,没问题
(gdb) print sum            # $2 = 0,sum还没加
(gdb) next                 # 执行一次循环体,sum变成0+1+1=2
(gdb) print sum            # $3 = 2,嗯,本来第一次应该加1得到1,现在多了1
(gdb) list                 # 看看源代码,发现sum += i + 1
(gdb) quit                 # 退出,去修改代码

预期正常行为:第一次循环,i=1sum应变为0+1=1。但这里变成了2,因此断定是i+1惹的祸。

?‍? 新手常见错误汇总

错误行为后果正确做法
忘记加-g编译无法设置断点、查看源码编译时使用-g
修改代码后没重新编译调试的还是旧版本每次修改后g++ -g ...重新编译
使用step进入cout等库函数陷入冗长的库代码next跨过,或设置断点跳过
断点设在空行或注释上无效断点,程序不停检查行号对应有效代码
run时忘记传参数(如果程序需要)程序因缺参数崩溃run 参数1 参数2传参

? 相关指引

  • GDB官方手册:在终端输入info gdb或访问 GDB Documentation
  • 图形前端工具gdbguidddCode::Blocks内置调试器,让你用鼠标点击设置断点
  • 进阶技巧:学会使用条件断点(break 10 if i == 5)、检查内存(x/10x &arr)等,能更快定位复杂bug
  • 其他调试器:如果你使用Visual Studio,它的“断点”、“逐语句”功能与GDB本质相同,只是界面不同

调试就像玩侦探游戏,多练习就能又快又准地找到bug!下次遇到程序结果不对,试着启动GDB,你也能成为代码世界的“福尔摩斯”。

例题精讲

1单选题

在GDB调试器中,若要在第10行的循环变量i等于5时暂停程序,应使用以下哪个命令设置条件断点?

Abreak 10 if i==5
Bbreak 10 when i==5
Cset breakpoint 10 condition i==5
Dbreak if i==5 at 10
2单选题

GDB中,`next`命令和`step`命令的主要区别是什么?

A`next`逐过程执行,`step`逐语句执行且会进入函数内部
B`next`向前执行,`step`向后执行
C`next`跳过循环,`step`进入循环
D`next`执行下一行,`step`执行上一行
3判断题

在GDB中,可以使用`display`命令在每次程序暂停时自动显示指定变量或表达式的值。

4填空题
在GDB调试过程中,要查看当前函数调用栈(即程序执行到当前位置时所有未被返回的函数调用序列),应使用命令 ___
5填空题
假设有如下C++代码:
int a = 0;
for (int i = 0; i < 10; i++) {
    a += i;
}
在GDB中,若要对变量a设置读观察点,即当a被读取时暂停程序,应使用命令 ___