调试你的程序
我觉得这是一个需要详细讨论的话题。调试是编程中一个非常重要的部分。如果你遇到错误,你需要知道如何找到问题并解决它。如果你只是缺少一个分号,你不应该为此发帖求助。
请记住,这并不是具体的调试方法。它不是一套规则,而是一组建议。我的建议可能不一定是正确的做法。如果在本文中发现任何不正确的内容,请告知我,以便我进行更正。我不喜欢传播错误的知识。
无论如何,我们将从基础开始,从识别和理解编译器错误,到使用 IDE 的调试器单步执行程序。
请注意:我参考了 Stephen Randy Davis 编写的《C++ for Dummies》第 5 版,第 139-155 页。
识别错误
通常,你的程序不会按计划工作,并且无法正确编译。即使是最好的程序员也会犯错误,能够识别你做错了什么是至关重要的。存在两种类型的错误:C++ 编译器可以自行捕获的错误,以及编译器无法捕获的错误。C++ 可以捕获的错误称为编译时错误。编译时错误应该相对容易修复,因为编译器会指出问题所在。编译器输出的所有垃圾信息都有其用途。这是一个例子。我忘记在 return 语句后添加分号。
1 2 3 4
|
int main()
{
return 0
}
|
你的编译器应该会生成类似这样的错误…
\main.cpp(4) : error C2143: syntax error : missing ';' before '}'
编译器错误因编译器而异,但总体上都差不多。在我的例子中,我使用的是 Visual Studio 2008,但如果你使用的是 Dev-C++ 或 G++,情况也是一样的。
现在让我们分解一下这个编译器错误。它的第一部分
\main.cpp(4)
表示错误在 main.cpp 文件中,第 4 行。之后是
error C2143:
,这是编译器特定的错误代码。如果你使用的是 Visual Studio,如果需要,你可以很容易地在 MSDN 上查找错误代码。之后,错误声明
syntax error :
,这告诉你你弄乱了一些语法。所以你一定没有正确输入某些内容。然后它告诉我
missing ‘;’ before ‘}’
在右括号前缺少一个分号。好的,我知道我缺少一个分号,我知道错误在第 4 行,我知道它在右括号之前。所以我转到 main.cpp,第 4 行,在右括号之前我需要一个分号。由于第 4 行上唯一的东西是一个右括号,我只需向上移动到第 3 行,OH!我注意到我忘记在
return 0
之后添加分号。识别编译器错误应该如此简单。
C++ 无法捕获的另一种类型的错误称为运行时错误。运行时错误通常更难捕获。
调试技巧
调试程序有几种方法。我最常使用的两种是 WRITE 技术和单步调试。首先,我将介绍 WRITE 技术。它涉及为你所有的变量创建输出语句,以便你可以查看所有变量的值。我将使用《C++ for Dummies》第 5 版中的这个程序示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
// ErrorProgram – This program averages a series
// of numbers, except that it contains
// a fatal bug.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char *pszArgs[])
{
cout << "This program is designed to crash!"
<< endl;
int nSum;
int nNums;
// accumulate input numbers until the
// user enteres a negative number, then
// return the average
nNums = 0;
while(true)
{
// enter another number to add
int nValue;
cout << "Enter another number:";
cin >> nValue;
cout << endl;
// if the input number is negative...
if(nValue < 0)
{
// ... then output the average
cout << "Average is: "
<< nSum/nNums
<< endl;
break;
}
// not negative, add the value to
// the accumulator
nSum += nValue;
}
cin.ignore(10000, '\n');
return 0;
}
|
执行此代码时,你会得到一个运行时错误。解决问题的简单方法是使用 WRITE 技术。每次你进入 while 循环时,让它输出 nNums 的值。
1 2 3 4 5 6 7
|
While(true)
{
// output
cout << “nNums = “ << nNums << endl;
// The rest of the program is unchanged
}
|
输出将如下所示
This program is designed to crash!
nNums = 0
Enter another number:1
nNums = 0
Enter another number:2
nNums = 0
Enter another number:3
nNums = 0
Enter another number: |
你可以看到 nNums 被初始化为 0,但是它在哪里递增?没有,这就是 bug。显然 nNums 应该在输入部分的每次循环中递增。通过使用 WRITE 技术,我们告诉程序输出每次循环中 nNums 的值,从而发现它没有被正确递增。
对于较小的程序,WRITE 技术效果很好,但是随着程序变得更大,输出所有变量变得更加困难,并且看起来只是浪费时间。相反,我们将依靠调试器。首先,让我们定义一个调试器。调试器是构建到大多数开发环境中的工具(尽管它们有所不同,但大多数调试器的工作原理相同)。程序员通过与编辑器相同的界面,通过命令来控制调试器。你可以在菜单项中或使用热键访问这些命令。调试器允许程序员控制他/她的程序的执行。他/她可以在程序中一次执行一个步骤,他/她可以在任何时候停止程序,并且他/她可以检查变量的值。要体会调试器的强大功能,你需要亲眼目睹它的实际应用。用语言很难解释(而且我不擅长用语言表达)。因此,有关调试的更多信息,我将链接到一个非常方便的网页。
http://www.cprogramming.com/tutorial/debugging_concepts.html
如果有任何需要添加的内容,请告诉我。