3.9 调试技巧

在理想环境下,因为有优秀的调试器能很容易使得程序的运行行为透明,所以可以很快发现错误。但是,大多数的调试器都有盲点,这就需要在程序中插入小段代码来帮助理解发生了什么问题。此外,可能在没有调试器(例如一个嵌入式系统)或者可能只有少量的反馈(如一个单行的LED显示屏)的环境下进行开发。在这些情况下,就要用创造性的方法去发现和显示关于程序执行情况的信息。下一节对程序调试的技巧提出某些建议。

3.9.1 调试标记

如果在程序中加入调试代码,可能引起不便。一开始得到了太多的信息,这使得很难把故障孤立出来。当认为已经找到了故障时,我们开始删掉调试代码,却有可能发现再需要这些代码。我们可以用两种标记解决这类问题:预处理器调试标记和运行期调试标记。

3.9.1.1 预处理器调试标记

通过使用预处理器#define定义一个或更多的调试标记(在头文件中更适合),可以测试一个使用#ifdef语句和包含条件调试代码的标记。当认为调试完成了,只需使用#undef标记,代码就会自动消失(这会减少可执行文件的大小和运行时间)。

最好在开始建立工程前决定调试标记的名字,这样名字会一致。为了区分预处理器标记和变量,预处理器标记一般用大写字母书写。一个常用的标记名是DEBUG(但是小心,不能使用NDEBUG,它是C中的保留字)。语句序列可以是:

3.9 调试技巧 - 图1

大多数C和C++的程序实现还允许在编译器的命令行中使用#define和#undef标记,所以可以用一个单独的命令重新编译代码并插入调试信息(最好使用makefile,这是后面要简要说明的工具)。具体细节请看局部的文档。

3.9.1.2 运行期调试标记

在某些情况下,在程序执行期间打开和关闭调试标记会更加方便,特别是使用命令行在启动程序时设置它们。只是为了插入调试代码来重新编译一个大程序是很乏味的。

为了自动打开和关闭调试代码,可以建立一个如下的bool标记:

3.9 调试技巧 - 图2

这个程序一直允许打开和关闭调试标记,直到输入“quit”告诉它想要退出。注意需要输入整个单词,而不仅仅是字母(如果想要的话,可以缩写它为字母)。在启动时,可以选择性地使用命令行参数打开调试—这个参数可以出现在命令行的任意地方,因为main()中的启动代码能看得到所有的参数。测试是相当简单的,因为表达式为:

3.9 调试技巧 - 图3

取得argv[i]字符数组并创建一个string使得它容易和==右端比较。上面的程序查找整个字符串—debug=on。也可以寻找—debug=,然后看它后面有什么,以提供更多的选择。本书的第2卷(可从www.BruceEckel.com中获得)有专门的一章讲述标准C++string类。

虽然调试标记是很少的几个领域之一,其中对于使用全局变量很有意义,但是,并不是说必须这样做。注意使用小写字母书写变量,用来提醒读者它不是一个预处理器标记。