NOIP学习小站
西安交通大学附属中学航天学校

程序的调试

当运行程序发现没有编译错误但是结果与预期不符的时候,往往是因为程序中出现了逻辑错误,有时候排查逻辑错误是一件痛苦的事情,有可能是不经意错误的写法(例如将判断相等写成了赋值语句),也有可能是对语言基础知识掌握不够好写出有漏洞的语句,也有可能是考虑不周全导致设计的算法有缺陷(例如遗漏了某些重要的操作),这里介绍一些常用的程序调试方法。

发现程序存在逻辑问题,首先应该分析输出结果,考虑程序可能在哪里出现问题。

例如预期的计算结果是小数,程序多次运行输出却一直都是整数,那么应该联想到是不是用了整数除以整数导致结果是整数。又例如前面的例题“P1425 小鱼的游泳时间”,如果直接小时数相减、分钟数相减,会发现结果中的分钟数可能会出现负数,很自然联想到分钟数直接相减,可能不够减导致出现负数,要解决这个逻辑错误,要么重新设计算法(顺序结构例题里有介绍),要么分钟数出现负数时用if语句来特别处理。

如果程序较复杂、代码较多,可能不容易马上想到哪里有问题,这个时候应该仔细阅读程序代码,尝试找出一些容易发现的问题,特别是初学者容易犯的错误。例如判断相等写成赋值(例如if(n==0)不小心写成if(n=0))、表示条件的关系比较错误使用了连续比较(例如本来应该用a<b && b<c,不小心写成了a<b<c)、未考虑运算优先级导致的错误(例如!(n==0)写成了!n==0)等等。

如果经过上面的步骤仍然没有排查出逻辑错误,那么还有一个简单的方法,就是追踪查看关键语句处关键变量的值。查看分析关键语句处关键变量的值,一般很容易找出逻辑出错的位置,这样就能进一步修改代码避免逻辑错误了。下面介绍两种追踪查看关键语句处关键变量的值的方法。

一、借助输出语句输出变量的值来调试程序

问题描述:输入简单的两个整数的五则运算表达式,例如1 + 2、345 * 498,输出计算结果

输入格式:输入内容包括两个整数和整数间的五则运算符号(只可能是+、-、*、/、%中的一个),整数和运算符号间用一个空格隔开。测试时保证输入的运算合法(例如不会出现除数为0的情况)。

输出格式:一个整数,就是输入的运算表达式的计算结果。

分析:可以使用switch多分支结构语句处理,根据输入的代表不同运算的字符对两个整数进行相应的运算得出结果。

#include<cstdio>
int main()
{
	int a,b,c;
	char op;
	scanf("%d%c%d",&a,&op,&b);
	switch(op){
		case '+':c=a+b;break;
		case '-':c=a-b;break;
		case '*':c=a*b;break;
		case '/':c=a/b;break;
		case '%':c=a%b;break;
	}
	printf("%d",c);
    return 0;
}

左边的程序使用了%d%c%d作为输入格式符,运行程序,按题目输入格式要求输入1 + 2时,运行结果如下:

4233934

很显然,运行的结果与预期不符,程序存在逻辑问题。初步排查程序,switch语句部分应该没有问题,最后的输出结果也没问题。是不是scanf语句输入到变量的值有问题?

在scanf输入语句后面添加printf输出变量a、op、b的值,运行查看结果以验证是否能正确输入。

#include<cstdio>
int main()
{
	int a,b,c;
	char op;
	scanf("%d%c%d",&a,&op,&b);
	printf("%d %c %d",a,op,b);
	return 0;//额外添加上这一句 
	switch(op){
		case '+':c=a+b;break;
		case '-':c=a-b;break;
		case '*':c=a*b;break;
		case '/':c=a/b;break;
		case '%':c=a%b;break;
	}
	printf("%d",c);
    return 0;
}

在scanf语句后添加了printf("%d %c %d",a,op,b);来输出变量a、op、b的值,甚至还添加了一句return 0;程序运行时执行printf语句后碰到return 0;就自动结束了,这样我们能够专注于查看printf输出的结果。

仍然输入1 + 2,此时运行结果如下:

1   6815400

变量a的值正常接收到了,变量op和b的值没有正常接收到。应该是输入格式的问题,修改输入语句为:scanf("%d %c %d",&a,&op,&b);再次运行,仍然输入1 + 2,此时运行结果如下(问题解决):

1 + 2

上面介绍的就是一种初学者常用的调试程序方法,通过添加额外的输出语句来查看变量的值,进而排查程序的逻辑问题。需要特别注意的是,额外添加的用于查看变量的值以调试程序的输出语句在问题解决后一定要删除掉哦,因为这些语句输出的内容也是程序的输出内容,在提交测评的时候会导致无法通过测评。

二、借助Dev C++的调试功能调试程序

Dev C++有调试程序代码的功能,通过调试可以在调试窗口查看变量的值,这样就不用像上面那样添加额外的输出语句来调试程序了。下面介绍Dev C++中调试程序的步骤:

1.在调试前编译程序(F9快捷按键)。

2.设置断点。断点的作用是在调试的时候能够自动暂停在断点处以方便查看变量的值。要在某行设置断点,只需要点击该行的行标即可:

如上图所示,我们想查看scanf执行后变量的值,所以在紧跟scanf的switch语句(第7行)的行标处点击一下,会发现行标处出现了一个红色小圆点,整行也被红色背景醒目标识,表示已经在第7行设置了断点。如果要取消断点,再次点击行标处即可。调试时可以设置多个断点。

3.添加要查看的变量。首先点击底部面板区域的“调试”选项卡,然后点击调试面板中的“添加查看”按钮,如下图所示:

在弹出来的“新变量”对话框中,输入调试要查看的变量的变量名,这里貌似只能一次输入添加一个变量名,要查看多个变量需要多次“添加查看”操作:

添加后,在左侧面板的“调试”选项卡下可以看到所有添加查看的变量:

4.进入调试。点击底部面板“调试”选项卡中的“调试”按钮,或者使用快捷键F5,进入调试状态:

调试也是运行,不过可以借助调试在设置的断点处自动暂停并查看变量的值,所以进入调试后会自动打开运行程序的命令行窗体,如果程序需要输入数据,那么就像运行程序一样,也要在命令行窗体中输入数据。

本例中,输入数据后会执行后面的switch语句,但是此处设置了断点,调试会在断点处自动暂停,如上图所示,此时可以在左侧面板的“调试”选项卡下查看变量的值以判断断点附近是否有逻辑问题(本例在此时可以看到变量op和b没有正确接收到输入的数据)。

调试会在设置的断点处自动暂停,如果要继续执行断点后面的语句,可以点击下部面板“调试”选项卡中的“下一步”按钮。如果要停止调试,可以点击“停止执行”按钮。