【发布时间】:2012-01-05 12:09:38
【问题描述】:
我想从我的代码中删除所有调试 printf 语句。如何通过打开/关闭调试语句而不使用条件调试来做到这一点?
我使用 printf 只是为了检查特定值是否正确...如下所示...: printf("value read %d\n", a);
【问题讨论】:
-
调试 printf 语句和“正常” printf 语句有什么区别吗?
我想从我的代码中删除所有调试 printf 语句。如何通过打开/关闭调试语句而不使用条件调试来做到这一点?
我使用 printf 只是为了检查特定值是否正确...如下所示...: printf("value read %d\n", a);
【问题讨论】:
如果不使用编辑器删除它们,您将无法做到这一点。
你可以做的是:
Allen Holub 在他的 book 中推荐 使用以下调试宏:
#ifdef DEBUG
# define D(x) x
#else
# define D(x)
#endif
并像这样使用它:
D(printf("oh, hai, debug"));
【讨论】:
DEBUG 没有定义时,D(...) 中的所有内容甚至在编译之前都会被(由预处理器)解散——即这些语句没有开销。而且,是的,这减少了可执行文件的大小。
将所有printf() 调用替换为DEBUG()。 DEBUG 是一个宏定义为:
#define DEBUG printf
这将调用真正的printf() 函数,或者你也可以将其定义为:
#define DEBUG fake_printf
然后调用 fake printf 来抑制调试信息。
fake printf 函数可能很蹩脚:
int fake_printf( const char * format, ... )
{
return 0;
}
【讨论】:
__builtin_printf 有一些关于printf 的内置知识),即使这样做,printf 内的所有函数调用都会被调用,你不希望这样.
fake_printf 的函数调用(例如log 或您自己计算的Pi 的第N 位)也会被调用,所以这并不正确。您不希望计算参数的副作用。
您应该解释如何进行调试printf。一个简单的方法可能是有一个宏像
bool debug_flag; // to be set in the debugger or at initialization
pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
#define debugprintf(Fmt,...) debugprintf_at(__FILE__,__LINE__,Fmt,__VA_ARGS__)
#ifndef NDEBUG
#define debugprintf_at(Fil,Lin,Fmt,...) do {if (debug_flag) { \
pthread_mutex_lock(&debug_mutex); \
fprintf (stderr, "%s:%d %s:" Fmt, Fil, Lin, __func__, \
##__VA_ARGS__); \
pthread_mutex_unlock(&debug_mutex); } \
} while(0)
#else
#define debugprintf(Fmt,...) do {} while(0)
#endif
(它使用互斥锁是因为您不想混合来自不同线程的调试 printf 消息;如果您不使用任何线程,请移除互斥锁及其锁定。)
如果您的问题是如何在一个庞大的软件(拥有其源代码)中找到所有调试 printf,您可以尝试使用 grep 或更高级的东西,如 GCC 插件或 MELT
延期。但是这种方法(GCC 定制)需要时间(一周或更长时间的工作),并且只对庞大的软件库(例如数百万行源代码)有价值。
对于不太大的软件,只需手动检查代码中的所有 printf,然后将您认为用于调试的那些替换为 debugprintf 宏调用。
【讨论】:
debugprintf() 宏在禁用调试时有效地删除了参数的评估和存储。在发布的其他解决方案中,仍然有一个空函数调用。
在 Visual Studio 中,如果 printf 在一行上,您可以使用正则表达式查找/替换来删除 printf 语句。好的,这将非常费力,但可能会奏效。
或者,编写另一个控制台应用程序来解析您的代码并为您执行此操作(然后您可以处理多行语句)。
在调试日志方面采用的一个很好的模式是用全局函数或宏替换整个代码中的 printf 语句并调用它。例如,如果您有一个全局 Log(const char* message) 函数(或宏),如果您想更改日志记录的操作方式,则可以更改该实现一次,而不是用预处理器语句乱扔代码。
比这更进一步,有许多logging frameworks(和here)执行相同的操作,但具有更多的可配置性。
【讨论】:
这是我使用的一种方法:
#if !defined ( NDEBUG )
void PrintMsg( const char_t* pFormat, ... )
{
va_list args;
va_start( args, pFormat );
(void)vprintf( pFormat, args );
va_end( args );
}
#else
void PrintMsg( const char_t*, ... )
{
}
#endif
#define DBG_PRINTF PrintMsg
然后将代码中的所有“printf”替换为“DBG_PRINTF”
它还将执行 printf 函数的参数。
【讨论】:
不确定这是否是问题。我个人喜欢为调试信息使用单独的函数/宏,它是通过预处理器指令启用的。 如果你想从生产代码中删除调试输出......想不出任何自动化的方式
【讨论】: