3.7 — 使用集成调试器:运行和断点 | C++调试指南

虽然单步执行(在第 3.6 课 — 使用集成调试器:单步执行中有介绍)有助于单独检查代码的每一行,但在大型程序中,逐行执行代码以到达你想要详细检查的点可能需要花费大量时间。

幸运的是,现代调试器提供了更多工具来帮助我们高效地调试程序。在本课中,我们将了解一些能让代码导航变得更加迅速的调试器功能。

运行到光标位置

第一个有用的命令通常被称为运行到光标位置。运行到光标位置命令会执行程序,直到执行到达光标所选的语句。然后它将控制权交还给你,以便你可以从该点开始调试。这是一种高效地从代码的特定点开始调试,或者在已经调试时直接跳到你想进一步检查的地方的方法。

对于 Visual Studio 用户

在 Visual Studio 中,可以通过右键单击代码中的语句并从上下文菜单中选择运行到光标,或者按 Ctrl-F10 快捷键组合来访问运行到光标位置命令。

对于 Code::Blocks 用户

在 Code::Blocks 中,可以通过右键单击代码中的语句并从上下文菜单或调试菜单中选择运行到光标,或者按 F4 快捷键来访问运行到光标位置命令。

对于 VS Code 用户

在 VS Code 中,可以在调试程序时通过右键单击代码中的语句并从上下文菜单中选择运行到光标来访问运行到光标位置命令。

让我们使用我们一直在使用的相同程序来尝试一下:

#include <iostream>

void printValue(int value)
{
    std::cout << value << '\n';
}

int main()
{
    printValue(5);

    return 0;
}

只需右键单击第 5 行的任意位置,然后选择"运行到光标"。

Code::Blocks安装

你会注意到程序开始运行,执行标记移动到你刚刚选择的行。程序已执行到这一点,并等待你进一步的调试命令。从这里开始,你可以逐行执行程序、将光标运行到另一个位置等。

如果你将光标运行到一个不会执行的位置,运行到光标位置将简单地运行你的程序直到终止。

继续执行

一旦你处于调试会话的中间,你可能只想从该点开始继续运行程序。做到这一点的最简单方法是使用继续命令。继续调试命令将按照正常情况继续运行程序,直到程序终止,或者直到某些东西触发控制权再次返回给你(例如断点,我们将在本课后面介绍)。

对于 Visual Studio 用户

在 Visual Studio 中,可以在调试程序时通过调试菜单 > 继续,或者按 F5 快捷键来访问继续命令。

对于 Code::Blocks 用户

在 Code::Blocks 中,可以在调试程序时通过调试菜单 > 开始 / 继续,或者按 F8 快捷键来访问继续命令。

对于 VS Code 用户

在 VS Code 中,可以在调试程序时通过运行菜单 > 继续,或者按 F5 快捷键来访问继续命令。

让我们测试一下继续命令。如果执行标记尚未在第 5 行,将光标运行到第 5 行。然后从这一点选择继续。程序将完成执行并终止。

开始

继续命令有一个名为开始的孪生兄弟。开始命令与继续执行相同的操作,只是从程序的开头开始。它只能在没有调试会话时调用。

对于 Visual Studio 用户

在 Visual Studio 中,可以在不调试程序时通过调试菜单 > 开始调试,或者按 F5 快捷键来访问开始命令。

对于 Code::Blocks 用户

在 Code::Blocks 中,可以在不调试程序时通过调试菜单 > 开始 / 继续,或者按 F8 快捷键来访问开始命令。

对于 VS Code 用户

在 VS Code 中,可以在不调试程序时通过运行菜单 > 开始调试,或者按 F5 快捷键来访问开始命令。

如果你对上面的示例程序使用开始命令,它将一路运行到底而不中断(除了在 VS Code 中,因为在上一课中我们将 stopAtEntry 设置为 true)。这可能看起来并不显眼,但那只是因为我们还没有告诉调试器中断程序。我们将在下一节中更好地利用这个命令。

断点

本节的最后一个主题是断点。断点是一个特殊的标记,它告诉调试器在调试模式下运行时在断点处停止程序的执行。

对于 Visual Studio 用户

在 Visual Studio 中,可以通过调试菜单 > 切换断点,或者右键单击语句并从上下文菜单中选择切换断点,或者按 F9 快捷键,或者点击行号左侧(灰色区域)来设置或移除断点。

对于 Code::Blocks 用户

在 Code::Blocks 中,可以通过调试菜单 > 切换断点,或者右键单击语句并从上下文菜单中选择切换断点,或者按 F5 快捷键,或者点击行号右侧来设置或移除断点。

对于 VS Code 用户

在 VS Code 中,可以通过运行菜单 > 切换断点,或者按 F9 快捷键,或者点击行号左侧来设置或移除断点。

当你设置一个断点时,你会看到一个新类型的图标出现。Visual Studio 使用一个红色圆圈,Code::Blocks 使用一个红色八边形(像一个停车标志):

Code::Blocks安装

继续操作,在第 5 行设置一个断点,如上图所示。

现在,选择开始命令让调试器运行你的代码,让我们看看断点的实际效果。你会注意到,程序没有一直运行到程序的结尾,而是在断点处停止了(执行标记位于断点图标上方):

Code::Blocks安装

就好像你将光标运行到了这一点。

与运行到光标位置相比,断点有几个优势。首先,断点会在每次遇到时都让调试器将控制权交还给你(与运行到光标位置不同,后者每次调用时只运行到光标一次)。其次,你可以设置一个断点,它会一直存在,直到你移除它,而使用运行到光标位置时,每次调用命令时你都需要定位到你想运行到的位置。

注意,放置在执行路径之外的行上的断点不会导致调试器停止代码的执行。

让我们来看一个稍微修改过的程序,更好地说明断点和运行到光标位置之间的区别:

#include <iostream>

void printValue(int value)
{
    std::cout << value << '\n';
}

int main()
{
    printValue(5);
    printValue(6);
    printValue(7);

    return 0;
}

首先,开始一个新的调试会话,然后将光标运行到第 5 行。现在选择继续。程序将继续运行到结尾(它不会再次在第 5 行停止,即使第 5 行还会被执行两次)。

接下来,在第 5 行放置一个断点,然后选择开始。程序将在第 5 行停止。现在选择继续。程序将第二次在第 5 行停止。再次选择继续,它将第三次停止。再选择一次继续,程序将终止。你可以看到,断点导致程序在该行被执行的次数一样多次停止。

设置下一条语句

还有一个调试命令使用得相当少,但至少值得了解一下,即使你不会经常使用它。设置下一条语句命令允许我们将执行点更改为其他语句(有时非正式地称为跳转)。这可以用来向前跳转执行点并跳过一些原本会执行的代码,或者向后跳转并重新执行已经执行过的内容。

对于 Visual Studio 用户

在 Visual Studio 中,可以通过右键单击语句并从上下文菜单中选择设置下一条语句,或者按 Ctrl-Shift-F10 快捷键组合来跳转执行点。这个选项是上下文相关的,只有在已经调试程序时才会出现。

对于 Code::Blocks 用户

在 Code::Blocks 中,可以通过调试菜单 > 设置下一条语句,或者右键单击语句并从上下文菜单中选择设置下一条语句来跳转执行点。Code::Blocks 没有这个命令的键盘快捷键。

对于 VS Code 用户

在 VS Code 中,可以通过右键单击语句并从上下文菜单中选择跳转到光标来跳转执行点。这个选项是上下文相关的,只有在已经调试程序时才会出现。

让我们看看向前跳转的实际效果:

#include <iostream>

void printValue(int value)
{
    std::cout << value << '\n';
}

int main()
{
    printValue(5);
    printValue(6);
    printValue(7);

    return 0;
}

首先,将光标运行到第 11 行。此时,你应该在控制台输出窗口中看到值 5。

现在,右键单击第 12 行,并选择设置下一条语句。这将导致第 11 行被跳过且不执行。然后选择继续以完成程序的执行。

程序的输出应如下所示:

5
7

我们可以看到printValue(6)被跳过了。

在几种情况下,此功能可能会很有用。

在我们对基本调试技术的探讨中,我们讨论了通过注释掉一个函数来确定该函数是否在导致问题方面发挥了作用。这需要修改我们的代码,并记住稍后取消注释该函数。在调试器中,没有直接跳过函数的方法,因此如果你想这样做,使用设置下一条语句跳过函数调用是实现这一目的的最简单方法。

向后跳转也可以在我们希望重新观察刚刚执行过的函数时发挥作用,以便我们可以看到它在做什么。

使用上面相同的代码,将光标运行到第 12 行。然后在第 11 行设置下一条语句,继续执行。程序的输出应为:

5
6
6
7

警告

设置下一条语句命令会更改执行点,但不会以其他方式更改程序状态。你的变量将保留跳转前的值。因此,跳转可能会导致程序产生与正常情况下不同的值、结果或行为。谨慎使用此功能(尤其是向后跳转)。

警告

你不应使用设置下一条语句将执行点更改为不同的函数。这可能导致未定义行为,并且很可能会导致崩溃。

“后退一步"与通过"设置下一条语句"向后跳转

“后退一步"会将一切状态倒回到最初从未超过该点时的状态。对变量值或其他程序状态所做的任何更改都将被撤销。这本质上是单步执行的"撤销"命令。

使用"设置下一条语句"向后跳转时,只会更改执行点。对变量值或其他程序状态所做的任何更改都不会被撤销。

总结

你现在学会了如何使用集成调试器来观察和控制程序的执行。虽然这些命令对于诊断代码流程问题(例如,确定某些函数是否被调用或未被调用)很有用,但它们只是集成调试器带来的好处的一部分。在下一课中,我们将开始探索检查程序状态的其他方法,而这需要你掌握这些命令作为先决条件。让我们继续学习吧!

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"