这里有另一种不在 FAQ 中的方法。它确实依赖于该但没有函数存在于您的库中,并且编译器能够优化掉无法执行的表达式。大多数现代库和编译器都具备此功能。即使缺少优化,该技巧仍然有效,尽管可执行文件会包含一些死代码。
我首先根据符号“DEBUG”的状态定义符号“debug”,如下所示:(这可以放在您的 .h 文件中)
#ifdef DEBUG #define debug _debug_call #else #define debug 1?0: #endif
然后我定义一个名为“_debug_call()”的函数,该函数接受一个或多个固定参数,后跟可变数量的参数。例如:
int _debug_call( int msglev, const char *msgfmt, ... )
该函数需要包含 stdarg.h 并定义一个变量
va_list arg_list;
然后,在执行诸如决定是否打印消息之类的代码之后(例如,我有不同的调试级别),我将执行:
va_start( arg_list, msgfmt ); vfprintf( stderr, msgfmt , arg_list ); putc( '\n', stderr );
现在,如果未定义符号 DEBUG,则定义符号 debug 为字符串“1?0:” 。当预处理器看到类似以下内容时:
debug( 1, "begin %s", argv[0] );它会将其转换为:
1?0:(1,"begin %s",argv[0]);这现在是一个?:求值左侧的表达式:但不是右侧。右侧只是一个带括号的表达式,如果对其进行求值,它将丢弃除最后一个之外的所有逗号分隔的表达式,只留下最后一个。但是?:导致其不被求值,并且一个好的编译器会识别出,由于 1 是一个常量,右侧的:永远不会被求值,甚至不会对其进行编译。它因此成为一个独立的(free standing)值。如果您选择分配debug()到类似
result = debug( 1, "begin %s", argv[0] );然后您将得到
result = 1?0:(1,"begin %s",argv[0]);其效果是
result = 0;现在,如果DEBUG被定义,那么它将变为
_debug_call(1,"begin %s",argv[0]);这样一来,它就被编译为一个函数调用。您确实需要将“DEBUG”的定义放在决定如何定义“debug”的预处理器代码之前。我通过将决定如何定义“debug”的部分放在一个名为 debug.h 的头文件中来实现这一点,并且我只需在包含 debug.h 之前定义或不定义符号“DEBUG”。
上面我提供了一个简单的版本。我实际的调试包要复杂一些,具有管理调试级别、输出文件以及一个名为trace的独立命名复制(与上面一样,但有一个新的名称,我可以使用它来表示一组不同的概念,以及一个单独的消息级别状态)。
如果任何人想将上述技巧包含在 FAQ 中或发布它,请随时这样做。
-- -- *-----------------------------* Phil Howard KA9WGN * -- -- | Inturnet, Inc. | Director of Internet Services | -- -- | Business Internet Solutions | eng at intur.net | -- -- *-----------------------------* phil at intur.net * --