假设你编写了一个程序,但它没有正常工作——代码编译没问题,但当你运行它时,却得到了不正确的结果。你一定在某个地方有语义错误。如何找到它呢?如果你一直遵循最佳实践,即编写少量代码然后进行测试,你可能对错误在哪里有一个很好的想法。或者你可能一无所知。
所有 Bug 都源于一个简单的前提:你认为正确的东西,实际上不是。真正找出错误在哪里可能具有挑战性。在本课中,我们将概述调试程序的通用过程。
因为我们还没有涉及太多 C++ 主题,所以本章中的示例程序将非常基础。这可能会让这里展示的一些技术显得多余。但是,请记住,这些技术旨在用于更大、更复杂的程序,并且在这种环境下(这是你最需要它们的地方)会更有用。
一种通用的调试方法
一旦问题被识别,调试问题通常包括六个步骤
- 找出问题的根本原因(通常是代码中不起作用的那一行)。我们将在下一课中讨论一些如何做到这一点的策略。
- 确保你理解问题发生的原因。
- 确定你将如何解决问题。
- 修复导致问题的问题。
- 重新测试以确保问题已修复。
- 重新测试以确保没有出现新问题。
我们在这里用一个现实生活中的类比。假设一个晚上,你去冰箱里的制冰机取冰。你把杯子放到制冰机旁边,按下杠杆,然后……什么也没出来。哦哦。你发现了一些缺陷。你会怎么做?你可能会开始调查,看看是否能找出问题的根本原因。
找出根本原因:既然你听到制冰机试图出冰,那可能不是制冰机构本身的问题。所以你打开冰箱,检查冰盘。没有冰。这是问题的根本原因吗?不,这是另一个症状。经过进一步检查,你确定制冰机似乎没有制冰。是制冰机的问题还是其他什么?冰箱仍然很冷,水管没有堵塞,其他一切似乎都在工作,所以你得出结论,根本原因是制冰机不工作。
理解问题:在这种情况下很简单。一个坏掉的制冰机不会制冰。
确定修复方法:此时,你有几种修复选项:你可以绕过问题(从商店购买袋装冰)。你可以尝试进一步诊断制冰机,看看是否有可以修复的部件。你可以购买一个新的制冰机并将其安装到当前制冰机的位置。或者你可以购买一个新的冰箱。你决定购买一个新的制冰机。
修复问题:制冰机到货后,你将其安装。
重新测试:重新通电并等待一夜后,你的新制冰机开始制冰。没有发现新问题。
现在让我们将这个过程应用于上一课中的简单程序
#include <iostream>
int add(int x, int y) // this function is supposed to perform addition
{
return x - y; // but it doesn't due to the wrong operator being used
}
int main()
{
std::cout << "5 + 3 = " << add(5, 3) << '\n'; // should produce 8, but produces 2
return 0;
}
这段代码有一个优点:Bug 非常明显,因为错误的答案通过第 10 行打印到屏幕上。这为我们的调查提供了一个起点。
找出根本原因:在第 10 行,我们可以看到我们正在传递字面量作为参数(5 和 3),所以那里没有出错的空间。由于函数 add 的输入是正确的,但输出不是,所以很明显函数 add 必须产生了错误的值。函数 add 中唯一的语句是 return 语句,它一定是罪魁祸首。我们找到了问题行。现在我们知道把注意力集中在哪里了,你很可能会通过检查发现我们正在进行减法而不是加法。
理解问题:在这种情况下,为什么会生成错误的值是显而易见的——我们使用了错误的运算符。
确定修复方法:我们只需将 operator- 更改为 operator+。
修复问题:这实际上是将 operator- 更改为 operator+ 并确保程序重新编译。
重新测试:实施更改后,重新运行程序将表明我们的程序现在生成了正确的值 8。对于这个简单的程序来说,这就是所有需要的测试。
这个例子很简单,但它说明了你在诊断任何程序时将经历的基本过程。