1.6.2 动态调试

在静态调试中可以发现和改正很多错误,但由于静态调试的特点,有一些比较隐蔽的错误还不能检查出来。只有上机进行动态调试,才能找出这些错误并改正它们。

1.编译过程中的调试

编译过程除了将源程序翻译成目标程序外,还要对源程序进行语法检查。如果发现源程序有语法错误,系统将显示错误信息。用户可以根据这些提示信息查找出错误性质,并在程序中出错之处进行相应的修改。有时我们会发现编译时有几行的错误信息都是一样的,但检查这些行本身又没有发现错误,此时要仔细检查与这些行有关的名字、表达式是否有问题。例如,因为程序中数组说明语句有错,这时,那些与该数组有关的程序行都会被编译系统检查出错。这种情况下,用户只要仔细分析一下,修改数组说明语句的错误后,许多错误也就会同时消失。对于编译阶段的调试,要充分利用屏幕给出的错误信息,对它进行仔细分析判断。只要注意总结经验,使程序通过编译是不难做到的。

例如,图1-7所示为对某个程序编译调试的结果图,从图中可知,第4行的变量i、第6行的变量sum没有定义,第8行丢掉了分号“;”。

图1-7 编译错误

排除编译错误时需注意两点:

(1)每次排除编译错误时最好从第一个错误开始排除,因为其他的错误有可能是因为第一个错误级联产生的。排除第一个错误之后重新编译,如果仍然有错,再从第一个错误开始排除。

(2)编译系统给出的某些错误提示信息,有时和实际出错原因并不相符,但提示的错误位置不会出错。因此要多多积累调试经验,提高调试技能。

2.连接过程中的调试

编译通过后要进行连接,连接的过程也有查错的功能,它将指出外部调用、函数之间的联系及存储区设置等方面的错误。如果连接时有这类错误,编译系统也会给出错误信息,用户要对这些信息仔细判断,从而找出程序中的问题并纠正之。连接时较常见的错误有以下几类:

(1)某个外部调用有错,通常系统明确提示了外部调用的名字,只要仔细检查各模块中与该名有关的语句,就不难发现错误。

(2)找不到某个库函数或某个文件,这类错误是由于库函数名写错,疏忽了某个库文件连接等。

(3)某些模块的参数超过了系统的限制。如模块的大小、库文件的个数超出要求等。

引起连接错误的原因很多,而且很隐蔽,给出的错误信息也不如编译时给出的直接、具体。因此,连接时的错误要比编译错误更难查找,需要仔细判断,而且对系统的限制和要求要有所了解。

3.运行过程中的调试

运行过程中的调试是动态调试的最后一个阶段。这一阶段的错误可分为两类:

(1)运行程序给出出错信息

运行时出错多与数据的输入、输出格式有关,与文件的操作有关,与程序运行时使用了没有分配给该程序的内存空间有关。

例如:

①利用scanf()函数输入数据,如scanf("%d",a);。假设此时a中的值为1000,则程序执行时会把输入的数据放到地址为1000的内存单元中,而不是放到变量a所占的内存单元中,从而造成运行错误。

②在使用数组元素a[i]时,变量i没有赋初值,或i的值已经超过数组a的下标范围。这时程序仍会按照公式“a+i×sizeof(a的数据类型)”计算需访问内存单元的地址,从而造成运行错误。

③指针变量p没有赋初值而直接使用*p。指针变量没有赋初值,则其值是一个随机数,而使用*p时则是访问以这个随机数为地址的内存单元,从而造成运行错误。

(2)运行结果不正常或不正确

出现这类错误比较难查出错原因,需要对整个程序进行全面检查。