3. 子函数在说明和定义时类型不一致。 4. 程序调用的子函数没有定义。 三、运行时的常见错误
1. 路径名错误。在MS-DOS中, 斜杠(\\)表示一个目录名; 而在Turbo C 中斜杠是个某个字符串的一个转义字符, 这样, 在用Turbo C 字符串给出一个路径名时应考虑\的转义的作用。例如, 有这样一条语句:
file=fopen(\
目的是打开C盘中NEW目录中的TBC.DAT文件, 但做不到。这里\后面紧接的分别是\及\及\将被分别编译为换行及tab字符, DOS将认为它是不正确的文件名而拒绝接受, 因为文件名中不能和换行或tab字符。正确的写法应为: file=fopen(\
2. 格式化输入输出时, 规定的类型与变量本身的类型不一致。例如: float l;
printf(\
3. scanf()函数中将变量地址写成变量。例如: int l;
scanf(\
4. 循环语句中, 循环控制变量在每次循环未进行修改, 使循环成为无限循环。 5. switch语句中没有使用break语句。 6. 将赋值号\误用作关系符\。 7. 多层条件语句的if和else不配对。
8. 用动态内存分配函数malloc()或calloc()分配的内存区使用完之后, 未用free()函数释放, 会导致函数前几次调用正常, 而后面调用时发生死机现象, 不能返回操作系统。其原因是因为没用空间可供分配, 而占用了操作系统在内存中的某些空间。 9. 使用了动态分配内存不成功的指针, 造成系统破坏。
10. 在对文件操作时, 没有在使用完及时关闭打开的文件。
附录二:C语言程序设计调试技术
C语言程序设计调试技术(一)—— 运行错误的判断与调试
通常所说的运行错误有两种,一种是逻辑错误,即程序实际运行结果和对程序结果的期望不符;另一种是程序设计上的错误,但它也躲过了编译程序和连接程序的检查,通常表现为突然死机、自行热启动或者输出信息混乱。
相对于编译和连接错误来说,运行错误的查找和判断更为困难。编译和连接错误分别由编译程序和连接程序检查,尽管有时它们报告的出错信息和错误的实际原因之间有一些差距,但总还可以作为查错时的一种参考。而运行错误就不同了,很少或根本没有提示信息,只能靠程序员的经验来判断错误的性质和位置。下面简单的介绍一些常见运行错误的调试方法。
一种逻辑错误是由于在设计程序的算法时考虑欠周到而引起的,如对边界和特殊情况未作妥善处理。例如下面所示的循环:
while(count) {
???.
count=count-1; }
程序员的构思是进行count次循环。但是,如果count中原来的值为负数时,此循环就成了一个“死循环”而导致无法停机,显然是错误的。但是编译程序无法查出这类错误,只有到了程序运行之后才有可能发现。再如,在利用海伦公式计算三角形面积时,首先应该检查给出的三条边长是否确实可以构成一个三角形,否则计算结果时没有意义的;而在编写求解一般实系数一元二次方程的程序时,必须在程序中设计处理复数情况的程序段,以免对负数求平方根。通常在手算时不用事先考虑这些问题,可以在确实发生了问题以后在提出解决的方法。但是程序是为计算机设计的,而计算机并没有自行应变的能力,程序员必须事先将一切可能遇到的情况考虑周全,尤其是对于那些受用户委托设计或者作为商品出售的软件更是如此。
另一种常见的逻辑错误是由于程序输入时的打字错误造成的,例如将判断条件中的“〉=”误输入为“〉”,将相等判断“= =”误输入为赋值号“=”等。含有这类错误的程序在运行时出现的故障现象多种多样,而且通常很难与错误的原因联系起来。
输入的数据中包含错误或者输入数据的格式不符合要求当然也会影响到程序的运行结果,特别是在数据量比较大,而又采用键盘直接输入数据时更容易产生这类错误。我们建议在数据量比较大时采用文件方式存放数据,程序通过文件读写来输入输出数据,这样可以通过编辑数据文件来修改其中的错误,并且在重复计算或者调试程序时也就不用反复输入数据了。这种方法非常适宜于科学和工程计算类应用程序中的数据处理。另一种方法是避免使用C语言的scanf( )函数输入数据,而代之以自行编写的、具有比较完善的数据校验功能的输入模块。这种方法通常用于数据处理、事务管理等比较复杂的应用程序的开发中,通常将数据输入和用户界面等模块结合起来统一进行设计,这需要较高的编程技巧。
C语言程序设计调试技术(二)——基本调试手段
程序的基本调试手段有以下几种:标准数据校验、程序跟踪、边界检查和简化循环次数等。下面分别介绍之。
? 标准数据校验:在程序编译、连接通过以后,就进入了运行调试阶段。运行调试的第一步就是用若干组已知结果的标准数据对程序进行检验。标准数据的选择非常重要,一是要有代表性,接近实际数据;二是比较简洁,容易对其结果的正确性进行分析。另外,对重要的临界数据也必须进行检验。
? 程序跟踪:对于比较复杂的大型程序来说,上述标准数据检验一次就完全通过的可能性很小。通常程序中总是存在许多各种各样的错误(就好像出错是程序的基本特性,一个错误也没有的程序反倒是罕见的意外),还需要多程序进行细致的调试工
作。
程序跟踪则是最重要的调试手段。程序跟踪的基本原则是让程序一句一句的执行(按F7 F8),通过观察和分析程序执行过程中数据和程序执行流程的变化查找错误。就Turbo C而言,程序跟踪可以采用两种方法,一种是直接利用其集成环境中的分布执行、断点设置、变量内容显示等功能对程序进行跟踪,这种方法留在下一单元的编程与调试部分介绍;另一种是传统方法,通过在程序中直接设置断点、打印重要变量内容等来掌握程序的运行情况。例如,可以在程序中的关键部位插入这样的代码段:
/*--调试代码段-------------------------------------------------*/ printf(“break point 10:line 120---count>100\\n”);
printf(“variable count=%d,x=%f,sum=%f\\n,count,x,sum”); getch();
/*--调试代码段结束-------------------------------------------*/
其中的变量可以根据程序的实际情况进行设计。使用getch( )函数的目的是要程序在执行到这一行时暂时停下来,从而可以让我们看清楚调试代码段所显示的信息。然后可以选择是否让程序继续执行。如果到这一断点时尚未发现错误,则可以按下任何一个键让程序继续运行到下一个断点;否则可使用组合键Ctrl+Break或者Ctrl+C键来中断程序,再使用编辑器对程序进行修改。在程序中的所有的问题都解决了之后,再将程序中所有的调试代码段统统删去,这种方法不但适用于 Turbo C,而且对于那些没有集成环境的C语言编译器来说就更为重要了。
? 边界检查:在设计检查用的数据时,要重点检查边界的特殊情况。例如,对于循环:
while(count<1000) {
????.. }
就应该设计数据检查count等于999、等于1000、等于0或者负数等情况。如果程序中有由if-else语句、switch语句等组成的分支结构,也应该设计相应的数据,使得分支中的每一条路径都要通过检查。 ? 简化:在调试时,有时可以通过对程序进行某种简化来加快调试速度。例如减少循
环次数、缩小数组规模、屏蔽某些次要程序段(如一些用于显示提示信息的子程序)等。但在进行简化工作时,一定要注意这种简化不能太过分,以至于无法代表原来程序的真实情况。例如,对于一个求解N元一次方程组的程序来说,仅将N等于2的情况调通是不够的,还不能保证该程序对于较大的方程组也能给出正确的结果。如果对于N=3或4的情况该程序也能正常工作,则在该程序中因为矩阵规模而出错的可能性就大大减少了(但这不说明该程序就一定没有错误了,例如如果该程序使用某种消元法,则还要考虑主对角线元素的绝对值过小或者为0的情况)。
C语言调试技术(三) -Turbo C 集成环境的调试功能
由于程序中存在着分支、循环等结构,造成了程序运行时的变化规律和其静态结构之间存在着一定的差异,因此仅靠阅读程序本身很难掌握程序运行时各变量内容的动态变化,这就给我们调试程序中的运行错误带来了很大的困难。如果能够在程序运行过程中动态地显示程序执行的流向和各变量的内容,则有助于程序员了解程序的动态运行情况,从而更好、更快地调试程序。TurboC集成环境有很强的动态调试能力,下面介绍其中最主要的几种手段。 (1)运行(Run: Run,ctrl-F9):运行程序员编写的应用程序。该选项的功能非常强大,如果源程序尚未编译,或者在编译以后又修改了源程序,则会在运行程序之前先自动对源程序进行编译和连接工作。如果源程序中设置有断点(参阅(2)),则只执行到断点处就停下来,以便程序员调试程序。若再次调用该选项,则从当前断点开始运行程序,直到程序结束或者到下一个断点处。 ·
(2)设置断点 (break/watch: toggle breakpoint, Ctrl-F8) :设置断点的作用是使程序可以分段运行。如果在程序中的某个语句处设置了断点,则使用上述运行选项执行程序时就会在断点处停下来,这时可以利用下面介绍的其它调试功能观察程序的运行情况,包括各数据区和变量的当前值。在程序中可以设置多处断点,这时每调用一次运行功能,则程序从当前位置执行到下一个断点处:如果断点是设置在循环中的,则每循环一次、程序就中断一次。为了管理断点,在集成环境的断点与观察(break/watch)子菜单中还有两个辅助功能:清除所有断点(clear all breakpoints)和查看下一个断点(View nextbreakpiont)。 (3)变量查看及修改 (Debug:Evaluate, CtrL-F4):该项功能用于在程序运行到断点处时查看变量或其它数据项的内容。对于变量来说,还可以改变其内容,便于下一步继续调试。在调用本功能时,屏幕上弹出一个窗口,窗口分为三栏:,最上面是设置(Eva1uate)栏,用于输入要观察的变量名或表达式;中间是结果(ResdO栏,用于显示要观察的变量或表达式的值;而最下方是修改(New va1ue)栏,用于修改变量的值。在查看或修改完毕时可以使用退出键(ESC)返回编辑状态。
(4)查看函数调用情况(Debug: Call stack, Ctrl-F3):该功能用于查看当前调用栈的情况。如果断点设置在函数中,则调用该功能会在屏幕上弹出一个窗口,显示出程序运行到断点时的函数调用顺序(最下方是主函数,最上方是当前正在执行的函数)。
(5)查找函数(Debug: Find function):可用于在程序中快速查找某个函数的位置。如果一个程序很大,或者包括多个源程序文件,则使用该功能是相当方便的。 调用该功能的结果是光标移到指定函数的开始。 (6)更新屏幕内容(Debug: Refresh disp1ay):在调试程序的过程中,有时程序的输出结果会破坏集成环境的编辑版面显示内容,这时可以使用该功能恢复正确的屏幕内容。 (7)设置观察对象(break/watch:Add watch,ctrl-F7):使用该项功能可以将变量或表达式设置为观察对象,这些观察对象的值在调试过程中会在屏幕下方的信息显示窗口中显示出来。该功能类似于上面介绍的“变量查看与修改(Evaluate)\功能,但更直观、更方便,只是不能修改变量的值、另外,在断点与‘观察(Break/watch)子菜单中还有几项用于管理观对象的功能选项:删除观察对象(Delete watch)选项 ,它用于删除一个观察对象;使用该项功能时,首先应使用屏幕窗口切换键(F6)将光标切换到信息显示窗口中,然后使用光标选定要删除的观察对象,再使用本功能选项删除选定的观察对象。编辑观察对象(Edit watch)选项,它用于修改观察对象,用法和删除观察对象相同。删除所有删除对象(remove all watches)选项,它可以删除所有的观察对象。
(8)执行到当前光标位置(Run: Go to cursor, F4):使程序执行到当前光标位置。
C语言调试技术(四)--图形程序运行的条件
Turbo C为用户提供了一个功能很强的画图软件库,它又称为BorLand图形接口(BGI),它包括图形库文件(graphics.lib),图形头文件(graphics.h)和许多图形显示器(图形终端)的驱动程序(如CGA.BGI、EGAVGA.BGI等)。还有一些字符集的字体驱动程序(如goth.chr黑体字符集等)。编写图形程序时用到的一些图形库函数均在graphics.lib 中,执行这些函数时,所需的有关信息(如宏定义等)则包含在graphics.h头文件中。因此用户在自已的画图源程序中必须包括graphics.h头文件,在进行目标程序连接时,要将graphics.lib连接到自己的目标程序中去。 图形系统的初始化
在编制图形程序时,进入图形方式前,首先要在程序中对使用的图形系统初始化,即要用什么类型的图形显示适配器的驱动程序,采用什么模式的图形方式(也就是相应程序的入口地址),以及该适配器驱动程序的寻找路径名。所用系统的显示适配器一定要支持所选用的显示模式,否则将出错。Turbo.C提供了一个图形系统初始化函数initgraph可完成这些功能。
图形系统的初始化函数
void far initgraph(int far *driver, int far *mode, char for path-for-driver);当我们使用的存储模式为tiny(微型)、 small(小型)或medium(中型)时,不需要远指针,因而可以将初始化函数调用格式写成如下形式(该说明适用于后面所述的任一函数): intitgraph(&graphdriver, &graphmode,,””); 其中驱动程序目录路径为空字符“”时,表示就在当前目录下,参数graphmode用所示的模式号或标示符来定义,参数graphdriver是一个枚举变量{DETECT,CGA,VGA,?},一般: graphdriver=DETECT
一旦执行了初始化,显示器即被设置成相应模式的图形方式。下面是某画图程序的开始部分,它包括对图形系统的初始化:
#include”graphics.h” main() {
int graphdriver=DETECT; int graphmode;
intgraph(& graphdriver, & graphmode,””);
上面初始化过程中,将由DETECT检测所用适配器类型,并将当前目录下相应的驱动程序装入,并采用最高分辩率显示模式作为graphmode的值。如检测到为CGA适配器时,则graphmode等于4或为CGAHI,若检测到VGA适配器,则graphmode等于2或为VGAHI。 图形程序运行的条件
由于图形程序运行并显示图象直接与显示器有关,而如何控制驱动显示器进行显示,Tubro C并没有向用户提供这种技术,而这也是不必要的,因为它与显示器硬件结构息息相关,编程者并不需要知道这些东西,否则太复杂了!但用户的图形程序要能运行并显示,则必须要包含有驱动显示器的这种程序。不同种类的显示器因硬件结构不同,因而驱动程序也不同,这些驱动程序已经在Tubro C系统盘上提供。在用户的图形程序中,进行图形系统初始化时,即执行函数。
initgraph(&graphdriver,&graphmode,char path-for-driver);
程序按照path-for-driver所指的路径将图形驱动程序装入内存。这样,以后的图形功能才能被支持。若在所指路径下找不到相应显示器的驱动程序,或没有对驱动程序进行装入操作,则运行图形程序时,就会在屏幕上显示出错信息:
BGI Error: Graphics not initialized(use”initgraph”) 当Tubro C2.0安装在软盘上时,没有安装上图形驱动程序(如CGA,BGI,EGAVGA.BGI等),所以必须在工作盘上复制上这些文件,否则图形程序就无法运行,而出现上述的错误信息。
那么,能不能不用egavga.bgi而运行图形程序,用以下步骤就可以: \\tc\\bgiobj egavga
\\tc\\tlib lib\\graphics.lib+egavga.obj
编程序时在用initgraph()之前先调用registerbgidriver(EGAVGA_driver);