数据结构课程设计任务书(5)

2019-01-19 12:15

相对于编译和连接错误来说, 运行错误的查找和判断更为困难。编译和连接错误可以由编译程序和连接程序检查, 而运行错误就不同了, 很少或根本没有提示信息, 只能靠程序员的经验来判断错误的性质和位置。下面简单地介绍一些常见运行错误的调试方法。

逻辑错误: 一种逻辑错误是由于在设计程序的算法时考虑欠周引起的, 例如对边界和极端条件未作处理等。例如以下循环:

while(count) {

… …

count = count?1;

}

程序员的构思是进行count次循环。但是, 如果count中原来的值为负数时, 此循环就成了一个“死循环”而导致无法停机, 显然是错误的。但是编译程序无法查出这类错误, 只有到了程序运行之后才有可能发现。再如, 在利用海伦公式计算三角型面积时, 首先应该确认给出的三条边长确实可以构成一个三角形, 否则计算结果是没有意义的; 而在编写求解一般实系数一元二次方程的程序时, 必须在程序中设计处理复根情况的程序段, 以免对负数求平方根。

另一种常见的逻辑错误是由于程序输入时的打字错误造成的, 例如将判断条件中的“>=”误输入为“>”, 将相等判断“==”误输入为赋值号“=”等。含有这类错误的程序在运行时出现的现象多种多样, 而且通常很难与错误的原因联系起来。

数组下标越界错误:即使用了并不存在的数组元素。例如有程序段

int a[5];

for(int i=1; i<=5; i++)

a[i] = 0;

由于[5]并不存在,而上述程序段试图将数据存放到一个并不存在的数组元素中,这会引起包括死机在内的严重运行错误。类似错误还有指针和动态存储分配引起的一些运行错误。

8.基本调试手段

程序的基本调试手段有以下几种: 标准数据检验、程序跟踪、边界检查和简化循环次数等。

标准数据检验: 在程序编译、连接通过以后, 就进入了运行调试阶段。运行调试的第一步就是用若干组已知结果的标准数据对程序进行检验。标准数据的选择非常重要, 一是要有代表性, 接近实际数据; 二是要比较简洁, 容易对其结果的正确性进行分析。另外, 对重要的临界数据也必须进行检验。

程序跟踪: 对于比较复杂的大型程序来说, 上述标准数据检验一次就完全通过的可能性很小。通常程序中总是存在许多各种各样的错误,还需要对程序进行细致的调试工作。程序跟踪则是最重要的调试手段。程序跟踪的基本原理是让程序一句一句地执行, 通过观察和分析程序执行的过程中数据和程序执行流程的变化来查找错误。就Visual C++而言, 程序跟踪可以采用两种方法, 一种是直接利用Developer Studio中的分步执行、断点设置、变量内容显示等功能对程序进行跟踪, 这种方法在后面的编程与调试部分介绍; 另一种是传统的方法, 通过在程序中直接设置断点、打印重要变量内容等来掌握程序的运行情况。例如,可以在程序中的关键部位插入这样的代码段:

// 调试代码段

cout << \cout << \ariable count = “ << count << “, x = “ << x << “, sum = “ << sum << endl;

_getch ( ) ;

// 调试代码段结束

其中的变量可以根据程序的实际情况进行设计。使用_getch ( ) 函数的目的是要程序在执行到这一行时暂时停下来, 从而可以看清楚调试代码段所显示的信息,然后选择是否让程序继续执行,该函数的原型在头文件conio.h中。如果到这一断点时尚未发现错误, 则可以按下任何一个键让程序继续运行到下一个断点; 否则可以使用组合键Ctrl+Break键来中断程序, 再使用编辑器对程序进行修改。在程序中的所有的问题都解决了之后, 再将程序中所有的调试代码段统统删去。

边界检查: 在设计检查用的数据时, 要重点检查边界和特殊情况。例如, 对于循环:

while(count<1000) { …

}

就应该设计数据检验count等于999、等于1000、等于0 或者负数等情况。如果程序中有由if-else语句、switch语句等组成的分支结构, 也应该设计相应的数据,使得分支中的每一条路径都要通过检验。

简化:在调试时, 有时可以通过对程序进行某种简化来加快调试速度。例如减少循环次数、缩小数组规模、屏蔽某些次要程序段 (如一些用于显示提示信息的子程序) 等。但在进行简化工作时, 一定要注意这种简化不能太过分, 以致于无法代表原来程序的真实情况。例如, 对于一个求解N元一次方程组的程序来说, 仅将N等于2的情况调通是不够的,还不能保证该程序对于较大的方程组也能给出正确的结果。如果对于N=3或4的情况该程序也能正常工作, 则在该程序中因为矩阵规模而出错的可能性就大大减少了。

9.注解号在调试中的作用

恰当的注解可以提高程序的可读性,是所有好程序的重要组成部分。不仅如此,在程序调试阶段, 注解还是一种很有用的调试工具。

在调试比较复杂的程序时, 可以使用将部分程序段落用注解号括起来, 以便于突出调试重点。例如在调试如下程序时:

y = func(x, y); ... ...

如果怀疑某运行错误是出在函数func()中, 则可以对程序作如下修改(假定0是该函数的预期的输出值之一):

y = 0; // func(x, y);

... ...

然后再次运行程序, 如果这次运行结果正常, 没有出现原先的错误现象, 则可以断定问题是出现于函数func()中; 如果运行结果仍然不正常, 则问题可能出现在后面的某个程序段落中。倘若后面的程序结构复杂,还可以通过在上述程序段落中多更换几种y值的方法进一步确定错误的性质。

又如在调试有数据输入的程序,如

cin >> a >> b >> c;

... ...

这样的程序时,为了突出测试该段程序对某组输入数据(如a = 1, b = 2, c= 3)的响应情况, 同时避免每次输入数据的麻烦, 可以利用注解号将上述程序段落改为:

// cin >> a >> b >> c;

a = 1;

b = 2;

c = 3; ... ...

这样在调试程序时就不必每次停下来等待输入数据了, 可以直接使用步进、跟踪或设置断点等手段调试这段程序。等到程序中的所有错误全部修正以后, 再恢复被修改的内容(即注解中的内容)即可。

10.条件编译

条件编译是编译预处理命令的一种,用于对源程序的内容进行选择性编译。例如, 在调试程序期间, 常常希望记录输出一些调试用的信息, 而在调试完成后, 就不再需要这些输出信息了。要解决这个问题,一种办法是逐一从源程序中删去这些输出这些调试信息的程序段落, 或者将这些程序段落用注释标记括起来。显然这样很不方便。另一种方法就是使用编译预处理中的条件编译命令。条件编译命令的格式为:

#if <条件1>

<程序段落1> #elif <条件2>

<程序段落2> ... ...

#elif <条件n>

<程序段落n> #else

<省缺程序段落>

#endif

条件编译中所使用的条件只能是由常数构成的表达式。如果该常数表达式的值不为0, 就表示条件成立, 否则表示条件不成立。一般情况下, 在#if中都是使用由#define指令产生的符号常数进行测试。

编译预处理命令的一个常见的用途是将调试代码插入应用程序中。程序员可以定义一个叫做DEBUG的符号常数, 值可以定义为1或者0。在程序中的任何地方,都可用以下方式插入调试信息:

#if DEBUG == 1

<此处为调试代码>

#endif

在开发程序时, 将DEBUG定义为1, 则插入的调试代码被编译进目标程序, 可输出帮助跟踪错误的信息。一旦程序工作正常, 就可将DEBUG重新定义为0后再次编译程序, 即可从目标程序中去掉调试代码。

C++中还有两个和#if类似的条件编译命令: #ifdef和#ifndef。例如

#ifdef DEBUG

则只要定义了DEBUG(无论将DEBUG定义为什么值,包括0), 甚至什么具体值也不是:

#define DEBUG

则上述条件成立。而在前面没有定义DEBUG时 #ifndef DEBUG 成立。

11.Developer Studio的跟踪调试功能

调试器是Developer Studio中最出色的部件之一,可以帮助找到在软件开发中可能遇到

的几乎每个错误。

调试器的主要调试手段有设置断点、跟踪和观察。所谓断点,即程序中的某处。在调试时使程序执行到断点处停下来,通过观察程序变量、表达式、调试输出信息、内存、寄存器和堆栈的值来了解程序的运行情况,或进一步跟踪程序的运行。

在当前编辑位置设置一个断点最直接的方式是使用快捷键F9,或用鼠标点击Build MiniBar工具条上的手形图标。断点用编辑窗口左边框上的大红圆点表示,非常醒目。取消一个断点的方法类似,只要在有断点的语句上重新使用快捷键F9即可取消已设置的断点。 如果已设置好了断点,则可通过子菜单Build/Start Debug调用调试器。该子菜单有4个选项,分别为: A. B.

Go(快捷键为F5):从当前语句开始执行程序,直到遇到一个断点或程序结束。用Go命令启动调试器时,从头开始执行程序。

Step Into(快捷键为F11):单步执行每一程序行,遇到函数时进入函数体内单

步执行。

C. Run To Cursor(快捷键为Ctrl+F10):运行程序至当前编辑位置。 D.

Attach To Process:将调试器与当前运行的其中某个进程联系起来,这样就可

以跟踪进入进程内部,就象调试项目工作区中当前打开的应用程序一样调试运行中

的进程。

当被调试的程序停在某个断点上时,编辑器左边框上的对应位置会出现一个黄色箭头指示被中断的语句。此时Developer Studio的版面布置会一些发生变化,如菜单栏中以调试(Debug)菜单项代替了建立(Build)菜单项并出现了一Debug工具栏。调试工具栏如图9所示。

图9 Developer Studio的Debug工具栏

Debug工具栏可分为4个区,第1,2两个区中是常用的调试命令,包括: (1)Restart(快捷键:Ctrl+Shift+F5):终止当前的调试过程,重新开始执行程序,停在程序的第1条语句处。

(2)Stop Debugging(快捷键:Shift+F5):退出调试器,同时结束调试过程和程序运行过程。

(3)Break Execution:终止程序运行,进入调试状态。多用于终止一个进入死循环的程序。

(4)Apply Code Changes(快捷键:Alt+F10):当源程序在调试过程中发生改变,重新进行编译。

(5)Show Next Statement(快捷键:Alt+Num *):显示下一语句。 (6)Step Into(快捷键:F11):跟踪。如果是一语句,则单步执行;如果是一函数调用,则跟踪到函数第一条可执行语句。

(7)Step Over(快捷键F10):单步执行。如果是一语句,则单步执行;如果是一函数调用,将此函数一次执行完毕,运行到下一条可执行语句。

(8)Step Out(快捷键:Shift+F11):从函数体内运行到外,即从当前位置运行到调用该函数语句的下一条语句。

(9)Run To Cursor(快捷键:Ctrl+F10):从当前位置运行到编辑光标。

第3区有一个眼镜图标(Quick Watch,快捷键为Shift+F9)用于弹出一个对话框,观察当前编辑位置的变量的值。

第4区有6个图标,分别用于激活6个调试器窗口:

(1)观察窗口(Watch)用于观察指定变量或表达式的值。可任意添加要观察的变量或表达式,并可用标签的形式(Watch1,Watch2,Watch3 等)增加多组观察对象。

(2)变量窗口(Variables)用于观察断点处或其附近的变量的当前值。Variables有3个标签,Auto标签显示变量和函数返回值,Locals标签显示当前函数的局部变量,this标签显示this指针对象。

(3)寄存器窗口(Register)用于观察在当前运行点的寄存器的内容。

(4)内存窗口(Memory)用于观察指定内存地址内容。

(5)调用栈窗口(Call Stack)用于观察调用栈中还未返回的被调用函数列表。调用栈给出从嵌套函数调用一直到断点位置的执行路径。

(6)汇编代码窗口(Disassmbly)用于显示被编译代码的汇编语言形式。

12.使用FileView标签

FileView标签用于显示当前项目中各项目之间的包含关系和项目中包含的所有文件。扩展顶层文件夹就可以显示项目的所有文件,如图10所示。

图10 FileView标签

FileView的操作功能:

1.定位:双击某个文件名或图标就可以打开相应的源程序编辑窗口。

2.添加文件:在项目中增加一个文件有多种方法,可以通过VC++的菜单选项Project/Add Project,也可以在文件列表上单击鼠标右键调出快捷菜单来增加一个文件。

3.删除文件:用鼠标在FileView列表中选择要去掉的文件,按删除键(Del)就会把这个文件从项目列表中去掉。

13.使用ClassView标签

默认情况下,当用户打开—个包含有C++类定义的工程时,ClassView窗口将作为工程工作空间的一部分出现,如图11所示。C1assView显示代表类和类成员的图标和名称。通过ClassView标签可以使成员函数、变量的定位和增加类、类的成员的工作变得较为容易。


数据结构课程设计任务书(5).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:地震英语词汇

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: