[原创]猜数字游戏的bug讨论,如何发现并修正bug。
猜数字游戏的bug讨论,如何发现并修正bug。作者:jhkdiy
之前大家都看过了《C++编写猜数字游戏》的文章了,整个源代码比较简单,有C++基础就可以看
明白程序的流程,但我的目的并不是在说说简单的C++语法复习和C++的实际应用,而是在于说一
下一直被很多初学者忽略的编程问题:如何发现并修正bug。很多学编程的朋友一般只要在编译时没有
语法错误能编译运行就万事大吉了,这样的不良习惯在以后会很严重。
那么我们一般怎样发现错误呢?错误不外乎这几种情况:第一个肯定是语法错误,这个比较容易解决,
因为一般编译器都会发现这样的错误并有明显的提示,我们只要找到错误行并修正就可以了,错误一般
是无意的拼写错误,符号错误等。
另一种情况是语法没有错误,但是程序就是没有达到自己预想的结果这一般是逻辑错误,而逻辑错误又
分为两种情况,一种是无意的逻辑错误,例如本来是想一个循环执行10次的,但是错误将结束条件写错了,而
导致执行了更多或更少,这种情况就要自己阅读源代码来解决了,因为自己了解程序的流程,所以一般都比较
容易发现。另一种情况是算法本身有问题,或更通俗的说是解决问题的方法有问题,设计思路有问题,这种情
况算是比较难发现的了。因为既不是语法错误,又不是自己的无意写错,代码更自己设想的流程都一样,但结
果就是不对。这种情况打败了很多编程人员,很多朋友在这种情况下只能到论坛发帖子了,找求救了,但很多
网友也一样爱莫能助。我个人认为这种情况还是自己研究好,当程序到了这种地步的时候就应该思考了:是我
的设计思路有问题吗?这个问题有没有其它方法可以解决?解决问题的方法通常都不止一个,当出现这种情况
的时候就应该考虑使用其它设计思路了。这也意味着一个大问题:你的代码需要重写,这对一般的小程序不成
问题,但如果重写导致整个项目的变动时就麻烦了,所以要慎重。
还有一种情况是环境错误和系统错误。这种情况可以说简单,而有时候又不简单。这种错误的表现是程序能
正常编译,但程序一执行就说“非法内存访问”或“此内存不能read”,跟着便是程序崩溃。相信很多朋友都遇过
这种情况,程序员最痛苦的事莫过于此。新手一般不懂得解决这种问题,因为他还不懂得怎样调试程序,这时到论坛
发帖求助是正确的。懂得调试的朋友就比较容易发现错误,当出现内存访问错误对话框时,可以记下导致程序错误
的指令地址,然后用调试软件载入程序,在导致程序错误的指令前下断点来执行,这时通常都会发现问题的所在。
只要修改一下代码就可以了。最后一种情况就是系统本身的问题,这种问题当你不知道原因的时候最难解决,但当
你知道原因的时候却最容易解决,这种情况就是程序的运行环境问题,一般学习语言的时候不会碰到,但编写实际
的应用软件时就会发生了,例如有些API函数只能在NT系列的系统上运行,win9x是不支持的,有很多新手不知道某个
函数的执行平台,老是发帖子问,但很多时候的回复就是简单的“该函数不能在该系统上运行”。解决这样的问题只
有找个替换的方法了,或者使用新的API,或者使用另一种方法。
错误的表现总是多种多样的,很多时候让人琢磨不透,但当问题解决的那一刹那却无比欣喜。这就是编程,让我
欢喜让我优。当出现问题时要试着自己解决,发现错误并修正它,在这个过程中你会学习到更多有趣的知识,而你的
编程经验也将在这里积累,所以编程能力的提高不单单是编写更多的程序,同时还要懂得调试程序。
好了,说了这么多,下面就讨论一下这个游戏存在的问题,首先这里不讨论语法错误问题,因为如果出现语法问题
证明你的基础不扎实。那么这个程序到底都有那些问题呢?在解说之前还是再看看代码吧:
/*
* 功 能:猜数字的游戏
* 编译环境:windows2000 + Dev-c++ 4.9
* 作 者:jhkdiy
* 电子邮件:jhkdiy_gzb@21cn.net
* 备 注:无意中看到《Beginning C++ game programming》一书,随便浏览了一下,
* 觉得这个游戏有点意思,所以自己完善了下。初学者用来学习一下语法
* 和提高一下兴趣还是很有意思的。该程序有BUG,大家找出来,
* 不是语法错误哦!!思考一下!
*/
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;
int main(int argc, char *argv[])
{
//产生随机数种子
srand(time(0));
int theNumber = rand() % 100 + 1; //随机数控制在1-100之间
int tries = 0, //用户尝试的次数
guess; //用户输入的数字
char bPlayAgain; //是否继续游戏
cout << \"\tWelcome to guess my number\n\n\";
do
{
//接受用户的输入
cout << \"Enter a guess: \";
cin >> guess;
++tries;
//如果输入的数字大于产生的随机数
if( guess > theNumber )
{
cout<<\"Too high!\n\n\";
}
//如果输入的数字小于产生的随机数
if( guess < theNumber )
{
cout << \"Too low!\n\n\";
}
//猜对了···
if( guess == theNumber )
{
cout << \"\n\n\tVery good! you get it!\"
<< \"\tThe number is: \" << theNumber << endl;
cout << \"\n\n\tyou try \" << tries << \" times!\" << endl;
//是否继续游戏
cout << \"\n\nDo you want to play again?(y/n)\";
cin >> bPlayAgain;
if( bPlayAgain == 'y' || bPlayAgain == 'Y')
{
//清屏后继续游戏
system(\"cls\");
continue;
}
else if( bPlayAgain == 'n' || bPlayAgain == 'N')
{
cout << \"\nSee you next time, bye!\n\";
break;
}
else
{
cout << \"\nEnter a wrong choice! program will exit!\n\";
break;
}
}
}while(true);
//退出程序
system(\"pause\");
return EXIT_SUCCESS;
}
程序第一次运行完全没有问题,但很多朋友很快就发现了一个bug,这个bug出现在:
//是否继续游戏
cout << \"\n\nDo you want to play again?(y/n)\";
cin >> bPlayAgain;
if( bPlayAgain == 'y' || bPlayAgain == 'Y')
{
//清屏后继续游戏
system(\"cls\");
continue;
}
程序直接清屏后就继续游戏了,竟然忘了,忘了什么?当第二次猜数字猜对的时候尝试
次数明显不对,明明只用4次猜对了,它竟然说尝试了9次。再看看代码,哦,原来在继续下
一次游戏之前没有对tries变量作初始化,这个容易修正:
//是否继续游戏
cout << \"\n\nDo you want to play again?(y/n)\";
cin >> bPlayAgain;
if( bPlayAgain == 'y' || bPlayAgain == 'Y')
{
//清屏后继续游戏
tries = 0;
system(\"cls\");
continue;
}
这下应该没问题了吧,第二次猜猜看,第三次猜猜看,咦??怎么每次猜的数字都是一样的?
再看看代码,又是刚才修改过的那段代码,程序中用来保存随机数的变量theNumber只在程序第一次
运行的时候赋值了,当第二次猜数字的时候它根本没有变,所以导致第二次以后的数字都是一样的。
好,知道问题所在后就好办了,继续修正:
//是否继续游戏
cout << \"\n\nDo you want to play again?(y/n)\";
cin >> bPlayAgain;
if( bPlayAgain == 'y' || bPlayAgain == 'Y')
{
//清屏后继续游戏
tries = 0;
theNumber = rand() % 100 + 1; //随机数控制在1-100之间
system(\"cls\");
continue;
}
好了,当程序第一次运行没问题,第二次、第三次也没问题了,自从修好后,腰不酸了,腿不疼了
嘿,还真灵···。真的没问题了吗?没问题啊,程序现在运行的好好的。呵呵,大家忽略了一个事实,
我们都假定用户的输入是可信赖的。问题就在这里,大家看看这段代码:
//接受用户的输入
cout << \"Enter a guess: \";
cin >> guess;
++tries;
程序假定用户输入的就是我们期望的数字,但是,如果用户输入的不是数字又会如何呢?实际情况吓
我们一跳,程序无限循环,已经无法控制了。这在实际的应用软件中经常出现,这就表明很多软件设计者
都不经意地作了很多假设:只要用户照我的使用方法做程序准没错。但是很多用户就是喜欢搞破坏,喜欢
搞垮程序。我们作为设计者应该要重视这个问题,对用户的输入不作任何自以为是的假设,对输入进行严格
的检查,只有符合程序运行的输入才作处理。那么这个程序应该如何修改呢?很简单,我们要对用户的输入
进行检查,提示用户只能输入数字:
//接受用户的输入
cout << \"Enter a guess: \";
cin >> guess;
//输入数据类型错误,非致命错误,可清除输入缓冲区挽回!
if( cin.rdstate() == ios_base::failbit )
{
system(\"cls\");
cout << \"Please enter numeric value!\n\"<< endl;
//使用clear()更改标记为正确后,同时也需要使用get()成员
//函数清除输入缓冲区,以达到重复输入的目的。
cin.clear();
cin.get();
continue;
}
++tries;
好了,再次编译运行程序,输入非数字字符,哦,程序检测到了,能正常运行。程序共
修改了三次,最后的代码如下:
/*
* 功 能:猜数字的游戏
* 编译环境:windows2000 + Dev-c++ 4.9
* 作 者:jhkdiy
* 电子邮件:jhkdiy_gzb@21cn.net
* 备 注:无意中看到《Beginning C++ game programming》一书,随便浏览了一下,
* 觉得这个游戏有点意思,所以自己完善了下。初学者用来学习一下语法
* 和提高一下兴趣还是很有意思的。该程序有BUG,大家找出来,
* 不是语法错误哦!!思考一下!
* 修 正:第二次运行tries变量没初始化,第二次运行theNumber变量没初始化,
* 当用户输入非数字字符时程序崩溃的问题。
*/
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;
int main(int argc, char *argv[])
{
//产生随机数种子
srand(time(0));
int theNumber = rand() % 100 + 1; //随机数控制在1-100之间
int tries = 0, //用户尝试的次数
guess; //用户输入的数字
char bPlayAgain; //是否继续游戏
cout << \"\tWelcome to guess my number\n\n\";
do
{
//接受用户的输入
cout << \"Enter a guess: \";
cin >> guess;
//输入数据类型错误,非致命错误,可清除输入缓冲区挽回!
if( cin.rdstate() == ios_base::failbit )
{
system(\"cls\");
cout << \"Please enter numeric value!\n\"<< endl;
//使用clear()更改标记为正确后,同时也需要使用get()成员
//函数清除输入缓冲区,以达到重复输入的目的。
cin.clear();
cin.get();
continue;
}
++tries;
//如果输入的数字大于产生的随机数
if( guess > theNumber )
{
cout<<\"Too high!\n\n\";
}
//如果输入的数字小于产生的随机数
if( guess < theNumber )
{
cout << \"Too low!\n\n\";
}
//猜对了···
if( guess == theNumber )
{
cout << \"\n\n\tVery good! you get it!\"
<< \"\tThe number is: \" << theNumber << endl;
cout << \"\n\n\tyou try \" << tries << \" times!\" << endl;
//是否继续游戏
cout << \"\n\nDo you want to play again?(y/n)\";
cin >> bPlayAgain;
if( bPlayAgain == 'y' || bPlayAgain == 'Y')
{
//清屏后继续游戏
tries = 0;
theNumber = rand() % 100 + 1; //随机数控制在1-100之间
system(\"cls\");
continue;
}
else if( bPlayAgain == 'n' || bPlayAgain == 'N')
{
cout << \"\nSee you next time, bye!\n\";
break;
}
else
{
cout << \"\nEnter a wrong choice! program will exit!\n\";
break;
}
}
}while(true);
//退出程序
system(\"pause\");
return EXIT_SUCCESS;
}
总结下,第一个和第二个错误属于逻辑错误,由于编程疏忽大意而造成的,但第三个错误
在用户不输入非数字字符前是永远不会被发现出来的,这就是程序的健壮性问题。
希望看了这篇文章后对大家有所帮助,有所觉悟,在编程的道路上更进一步。
下面是修改后完整的代码和执行程序。
http://jhkdiy.go3.icpcn.com/code/download/gessnumber.rar
http://jhkdiy.go3.icpcn.com/code/download/gessnumberfixed.rar