发布
2010年9月19日

调试你的程序

评分:3.3/5 (27 票)
*****
调试你的程序

我觉得这是一个需要详细讨论的话题。调试是编程中一个非常重要的部分。如果你遇到错误,你需要知道如何找到问题并解决它。如果你只是缺少一个分号,你不应该为此发帖求助。
请记住,这并不是具体的调试方法。它不是一套规则,而是一组建议。我的建议可能不一定是正确的做法。如果在本文中发现任何不正确的内容,请告知我,以便我进行更正。我不喜欢传播错误的知识。

无论如何,我们将从基础开始,从识别和理解编译器错误,到使用 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

如果有任何需要添加的内容,请告诉我。