第六篇:使用预处理器帮助调试
前言
你是否遇到过以下情况?
情况一:为了调试方便,代码中夹杂各种cout语句。当调试好了,把这些语句删了,运行“正式版”后,又发现新问题,只得把这些cout语句一个个添加回去再进行调试。如此不断循环。
情况二:希望在代码中获取到源文件的文件名,当前代码行号,编译时间等信息。
情况三:纠结于是否实现某些概率极低(几乎不存在)的错误检测
如果有,那么这篇随笔适合你,或者说,预处理器带来的调试技术适合你。
技巧一:设置调试区代码开关
请看下面的源代码:
1 #include <iostream> 2 3 using namespace std; 4 5 int main() 6 { 7 /* 8 * 源代码区一 9 */ 10 11 #ifdef DEBUG1 12 cout << "DEBUG1" << endl; 13 #endif 14 15 /* 16 * 源代码区二 17 */ 18 19 #ifdef DEBUG2 20 cout << "DEBUG2" << endl; 21 #endif 22 23 /* 24 * 源代码区三 25 */ 26 27 return 0; 28 }
两段包含cout语句的代码段都是调试语句,而两个宏DEBUG1和DEBUG2就是所谓的开关。现在假设我想执行上面的那段调试代码,则可以输入以下指令打开DEBUG1开关并完成编译(这条命令等效于在源文件开头加上#define DEBUG1再编译):
1 g++ -DDEBUG1 1.cpp -o run
运行结果如下:
结果显示DEBUG1开关对应的调试语句得到执行。可以用同样的方法操纵开关DEBUG2及其对应调试代码。明白了吧,你可以通过打开开关自由地选择需要编译并运行的调试区代码。
技巧二:使用预定义宏获取相关信息
预处理器提供了一些预定义的宏可获取编译,文件的相关信息,参见下面代码:
1 #include <iostream> 2 3 using namespace std; 4 5 int main() 6 { 7 cout << "文件名: " << __FILE__ << endl; 8 cout << "当前行号: " << __LINE__ << endl; 9 cout << "编译日期: " << __DATE__ << endl; 10 cout << "编译时间: " << __TIME__ << endl; 11 12 return 0; 13 }
运行结果:
技巧三:使用assert断言宏确保某个条件不发生
基本格式为 assert(表达式)。当表达式为真,语句不做任何事情,否则语句输出错误并终止程序执行,请看下面代码:
1 #include <iostream> 2 #include <cassert> // 要使用assert断言宏必须包含这个头文件 3 4 using namespace std; 5 6 int main() 7 { 8 int t=0; 9 cin >> t; 10 11 assert (t !=0); 12 cout << "t: " << t << endl; 13 14 return 0; 15 }
assert在这里的作用是确保t不等于0。如果t=0,程序会弹出错误并停止运行。编译代码并运行,
当输入为0时运行结果:
当输入其他数字时运行结果:
最后要强调的是,assert只是调试工具,它绝对不能代替逻辑检查参与到异常处理中。