您当前的位置:首页 > 计算机 > 编程开发 > VC/VC++

C++调试过程中最麻烦的问题―运行时错误

时间:01-10来源:作者:点击数:

在调试过程中,运行时错误是最麻烦的问题。因为编译错误可以由编译器检查出来,而大多数编译器对运行时错误却无能为力。查错和纠错的工作完全由用户自己来完成。

运行时错误还分为两种:

一种是由于考虑不周或输入错误导致程序异常(Exception),比如数组越界访问,除数为零,堆栈溢出等等。另一种是由于程序设计思路的错误导致程序异常或难以得到预期的效果。

对于第一类运行时错误,我们不需要重新设计解决问题的思路,认为当前算法是可行的、有效的。我们只需要找出输入的错误或考虑临界情况的处理方法即可。对于第二类运行时错误,不得不遗憾地说,一切都要从头再来。

见识运行时错误

由于编译器无法发现运行时错误,这些错误往往是在程序运行时以五花八门的形式表现出来。下面就是典型的几种因运行时错误引起的问题:

(1)WindowsXP错误报告

(2)内存不能为Read/Written

(3)非法操作

(4)Debug错误

查找错误点

语法错误的位置能很快地被编译器找到,而运行时错误的位置却很难被我们发现。即使我们一条条地检查语句,也未必能检查出什么。所以,在这里要介绍一种查找导致运行时错误的语句的方法。

我们知道,带有运行时错误的程序是可以运行的。当它运行到一个产生错误的语句时,就提示出错了。根据这个特点,我们可以用输出语句来判断程序的运行流程。下面就让我们来看一段有运行时错误的程序:(程序11.4)

#include <iostream>
using namespace std;
int main()
{
   char a[5],b[5];
   int alen=0,blen=0;//记录字符串a和b的长度
   cin >>a >>b;
   for (int i=0;a[i]!='\0' && b[i]!='\0';i++)//计算字符串的长度
   {
      if (a[i]!='\0')
         alen++;
      if (b[i]!='\0')
         blen++;
   }
   char *c=new char[alen+blen];//申请堆内存,存放连接后的字符串
   for (i=0;i<=alen+blen;i++)//把字符串a和b连接复制到字符串c
   {
      if (i<alen)
         c[i]=a[i];
      else
         c[i]=b[i-alen];
   }
   cout <<c <<endl;
   delete [] c;//释放堆内存
   return 0;
}

运行结果:
OOTTMA
TomatoStudio
udioTomat  葺葺葺葺

在程序运行结束之前,提示Debug Error,它属于一种运行时错误。而且根据输出的一些内容,发现程序也没有达到连接字符串的目的。所以我们让程序输出更多信息,查找错误原因。首先在计算字符串a和b的长度后,输出他们的长度,即在第一个for语句后添加一句cout <<"alen=" <<alen <<"blen=" <<blen <<endl;。

运行结果:
OOTTMA
TomatoStudio
alen=4blen=4
udioTomat  葺葺葺葺

OOTTMA字符串长为6,TomatoStudio字符串长为12。根据程序运行结果,我们发现计算出的字符串长度有问题。所以我们必须检查实现该功能的语句。另外,由字符串长度我们可以想到申请空间是否足够的问题。发现数组的空间只能存放5个字符,而现在两个字符串都已经超过这个限制。于是把数组空间扩大,该作char a[20],b[20];。

运行结果:
OOTTMA
TomatoStudio
alen=6blen=6
OOTTMATomatoS  葺葺癅

发现字符串a的长度已经正确,可是字符串b的长度为什么不对呢?经过多次尝试,我们发现,正确的字符串长度总是较短的字符串。所以我们想到检查循环继续的条件是否正确,如果过早地终止循环,就会导致这种情况。果然,a[i]!='\0' && b[i]!='\0'意味着只要有一个字符串结束,那么长度计算就结束了,故把&&改成||。

运行结果:
OOTTMA
TomatoStudio
alen=35blen=41
OOTTMA

这么一改,居然两个长度全都错了。我们不禁要思考为什么会这样了:用一个for语句来计算两个字符串的长度,当循环变量越过任一个字符串的结尾符以后又误认为它没有结束,所以输出的长度远远长于字符串的实际长度。我们把计算字符串长度用两个for语句来实现。即程序被改写成这样:

#include <iostream>
using namespace std;
int main()
{
   char a[20],b[20];
   cin >>a >>b;
   for (int alen=0;a[alen]!='\0';alen++);//计算字符串a的长度
      for (int blen=0;b[blen]!='\0';blen++);//计算字符串b的长度
         cout <<"alen=" <<alen <<"blen=" <<blen <<endl;
   char *c=new char[alen+blen];
   for (int i=0;i<=alen+blen;i++)
   {
     if (i<alen)
        c[i]=a[i];
      else
        c[i]=b[i-alen];
   }
   cout <<c <<endl;
   delete [] c;
   return 0;
}

运行结果:
OOTTMA
TomatoStudio
alen=6blen=12
OOTTMATomatoStudio

现在两个字符串的长度都正确了,输出的内容也实现了字符串的连接,但是Debug Error仍然存在。继续检查,发现剩下的语句和申请的堆内存空间字符串c有关了。于是先检查c是否有越界访问。根据c申请的空间大小,发现for语句中循环继续的条件有错误,导致越界访问,把它改成i<alen+blen;。

运行结果:
OOTTMA
TomatoStudio
alen=6blen=12
OOTTMATomatoStudio  @

Debug Error已经没有了,看来造成这个错误的原因就是越界了。但是现在输出的字符串后面有乱码,可能是结尾符被忽略了。检查程序,发现alen+blen是两字符串长度,但是没有考虑结尾符,所以要给字符串c增加一个字符的空间。

程序改写成如下:

#include <iostream>
using namespace std;
int main()
{
   char a[20],b[20];
   cin >>a >>b;
   for (int alen=0;a[alen]!='\0';alen++);
      for (int blen=0;b[blen]!='\0';blen++);
         //cout <<"alen=" <<alen <<"blen=" <<blen <<endl;
         char *c=new char[alen+blen+1];
   for (int i=0;i<alen+blen+1;i++)
   {
      if (i<alen)
         c[i]=a[i];
      else
         c[i]=b[i-alen];
   }
   cout <<c <<endl;
   delete [] c;
   return 0;
}

运行结果:
OOTTMA
TomatoStudio
OOTTMATomatoStudio

至此,程序修改完成。在目前的测试数据下,不再出现运行时错误,并且也能实现字符串连接的功能。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门