GDB

GDB 调试入门

GDB 调试入门

本节课目标:让学生理解 Debug 的基本思想,学会在 OnlineGDB 中设置断点、单步执行、观察变量,并能用调试方法发现循环边界错误、变量初始化错误和数组下标错误。

一、为什么要学习调试?

写程序时,错误不一定都是语法错误。很多时候,程序能够运行,但是结果不对,这类错误叫做逻辑错误

普通运行程序时,我们只能看到最后的输入和输出,看不到程序中间是怎么执行的。调试工具的作用,就是让程序”慢下来”,帮助我们一行一行观察程序的执行过程。

教师讲法:Run 只能看到结果,Debug 可以看到过程。程序调试不是让电脑替我们思考,而是帮助我们看清楚程序到底执行到了哪里,变量到底变成了什么。

二、GDB 和 OnlineGDB 是什么?

GDB 是常用的程序调试工具,可以帮助我们设置断点、单步执行、查看变量值。

OnlineGDB 是一个在线编程和调试网站,不需要本地安装环境,适合课堂演示。

课堂演示网址:

https://www.onlinegdb.com/

进入网站后,左上角语言选择:

C++17 或 C++20

三、断点是什么?

断点就是让程序运行到某一行时先暂停。

注意:断点不是程序的起点。程序仍然会从 main 函数开始运行,只是运行到断点所在行时暂停。

核心理解:
程序从 main 开始执行 → 正常运行前面的代码 → 到达断点所在行 → 暂停 → 开始观察变量和单步调试。

例如:

int n;
cin >> n;

int sum = 0;   // 如果这里打断点,程序会运行到这一行暂停
for (int i = 1; i <= n; i++) {
    sum += i;
}
cout << sum << endl;

如果断点打在 int sum = 0; 这一行,那么 cin >> n; 已经执行过了,所以 n 已经有输入值。

四、GDB 常用命令速查表

GDB 命令 简称 作用 OnlineGDB 对应操作
break b 在指定行设置断点 点击行号左侧
next n 单步执行(不进入函数内部) Step Over 按钮
step s 单步执行(进入函数内部) Step Into 按钮
continue c 继续运行到下一个断点或程序结束 Continue 按钮
print p 查看变量的当前值 Variables 面板自动显示
display 每次暂停时自动显示某个变量的值 在 Variables 面板中固定观察
list l 显示当前暂停位置附近的源代码 代码编辑区自动高亮
info locals 显示当前作用域所有局部变量 Variables 面板自动列出
quit q 退出 GDB 点击 Stop 或关闭页面
课堂提示:在 OnlineGDB 中,大部分操作都可以通过界面按钮完成,不需要手动输入 GDB 命令。但是理解这些命令的含义,有助于将来使用命令行 GDB 或在其他 IDE 中调试。

五、OnlineGDB 演示流程

  1. 打开 https://www.onlinegdb.com/
  2. 语言选择 C++17C++20
  3. 输入示例代码。
  4. 先点击 Run 普通运行,观察最终输出。
  5. 在代码左侧行号处点击,设置断点。
  6. 点击 Debug 进入调试模式。
  7. 使用 Step Over 一行一行执行。
  8. 观察变量 isuma[i] 等的变化。
  9. 使用 Continue 继续运行到下一个断点或程序结束。

六、第一个演示:累加求和

下面的程序计算 1 + 2 + ... + n

#include 
using namespace std;

int main() {
    int n;
    cin >> n;

    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }

    cout << sum << endl;
    return 0;
}

1. 普通运行

输入:

5

输出:

15
教师提问:我们看到了结果 15,但是程序是怎么一步一步得到 15 的?每一轮循环中 isum 分别是多少?

2. 设置断点

建议把断点设置在这一行:

sum += i;

这样每次循环执行到累加语句时,程序都会暂停,方便观察 isum

3. 观察变量变化

输入 5 时,变量变化大致如下:

循环轮次 i 执行 sum += i 后的 sum
第 1 次11
第 2 次23
第 3 次36
第 4 次410
第 5 次515
课堂重点:调试时不要只看代码,要重点观察变量变化。循环问题的本质,往往就是循环变量和关键变量的变化过程。

七、Step Over 和 Continue 的区别

按钮 作用 课堂解释
Step Over 执行当前这一行,然后停到下一行 一步一步走
Continue 从当前位置继续执行,直到遇到下一个断点、程序结束、程序出错或等待输入 一路跑,直到遇到下一个红灯
推荐讲法:Step Over 像"慢慢走路",Continue 像"继续跑步"。如果前面还有断点,Continue 会在下一个断点停下;如果没有断点,就会直接运行到程序结束。

八、变量是不是都会显示?

不是所有变量都会显示。调试时通常只能看到当前程序暂停位置能够访问到的变量

简单理解:Debug 看到的变量,不是全程序所有变量,而是"当前程序走到这里时,活着的变量"。还没定义的变量看不到,已经离开作用域的变量也看不到。

例如:

#include 
using namespace std;

int main() {
    int n;
    cin >> n;

    int sum = 0;

    for (int i = 1; i <= n; i++) {
        int temp = i * 2;
        sum += i;
    }

    cout << sum << endl;
    return 0;
}
  • 程序停在 cin >> n; 附近时,sum 可能还没有定义或还没有初始化。
  • 程序停在循环内部时,通常可以看到 nsumitemp
  • 程序出了循环以后,itemp 通常就看不到了,因为它们的作用域已经结束。

九、用 Debug 找循环边界错误

把正确程序中的循环条件故意改错:

for (int i = 1; i < n; i++) {
    sum += i;
}

输入:

5

错误输出:

10

原因是 i < n 只会让 i4,不会加到 5

教师引导:让学生使用断点和 Step Over 观察 i 的变化,发现 i 没有到 5,最终定位到循环条件错误。

十、第二个演示:数组中找最大值

下面程序用于找到数组中的最大值:

#include 
using namespace std;

int main() {
    int a[5] = {3, 1, 4, 1, 5};
    int maxx = a[0];

    for (int i = 1; i < 5; i++) {
        if (a[i] > maxx) {
            maxx = a[i];
        }
    }

    cout << maxx << endl;
    return 0;
}

建议断点打在:

if (a[i] > maxx) {

重点观察:

  • i 当前是多少
  • a[i] 当前是多少
  • maxx 是否需要更新
i a[i] 当前 maxx 是否更新
113不更新
243更新为 4
314不更新
454更新为 5

十一、常见错误与调试思路

常见错误 表现 调试时重点观察
循环少执行一次 结果偏小或漏掉最后一个元素 i 是否到达目标值
循环多执行一次 数组越界或结果异常 i 是否超过合法范围
变量忘记初始化 结果随机、每次可能不一样 sumcntmaxx 的初始值
数组下标越界 程序崩溃或结果奇怪 数组下标是否在 0n-1 之间
if 条件写反 更新逻辑错误 条件成立时,程序是否进入了正确分支

十二、课堂练习

练习 1:找出循环边界错误

下面程序想计算 1 + 2 + ... + n,但结果错误。请使用 Debug 找出原因。

#include 
using namespace std;

int main() {
    int n;
    cin >> n;

    int sum = 0;
    for (int i = 1; i < n; i++) {
        sum += i;
    }

    cout << sum << endl;
    return 0;
}

提示:输入 5,观察 i 有没有到达 5

练习 2:找出最大值程序的问题

下面程序想找到最大值,但当数组全是负数时会出错。请使用 Debug 观察 maxx 的变化。

#include 
using namespace std;

int main() {
    int a[5] = {-3, -8, -2, -6, -5};
    int maxx = 0;

    for (int i = 0; i < 5; i++) {
        if (a[i] > maxx) {
            maxx = a[i];
        }
    }

    cout << maxx << endl;
    return 0;
}

思考:为什么 maxx 一开始不能随便设成 0?应该如何修改?

练习 3:观察数组下标

下面程序存在数组下标问题。请使用 Debug 观察 ia[i]

#include 
using namespace std;

int main() {
    int a[5];

    for (int i = 1; i <= 5; i++) {
        cin >> a[i];
    }

    for (int i = 1; i <= 5; i++) {
        cout << a[i] << " ";
    }

    return 0;
}

提示:C++ 中长度为 5 的数组,合法下标是 0, 1, 2, 3, 4

十三、本节课总结

  • 断点是程序的暂停点,不是程序的起点。
  • 程序会从 main 开始运行,到断点所在行暂停。
  • Step Over 是一步一步执行。
  • Continue 是继续运行到下一个断点、程序结束、程序出错或等待输入。
  • 调试时显示的变量,是当前作用域中已经存在、可以访问的变量。
  • Debug 的核心不是记按钮,而是观察变量变化,找到错误发生的位置。

十四、给学生的一句话

程序调试的核心:设置断点,让程序停下来,然后一步一步执行,观察变量变化,找到程序出错的位置。

建议本节课安排在学生学完循环和一维数组之后,作为"程序调试与错误定位"专题讲解。后续在函数、递归、搜索、动态规划等内容中继续穿插使用 Debug。