似乎为printf
风格的调试人们总是用预处理宏。与此类似的解决方案有什么问题吗?使用调试宏而不是简单函数有什么好处?
void debug(char *msg) {
#ifdef DEBUG
printf("%s", msg);
#endif
}
似乎为printf
风格的调试人们总是用预处理宏。与此类似的解决方案有什么问题吗?使用调试宏而不是简单函数有什么好处?
void debug(char *msg) {
#ifdef DEBUG
printf("%s", msg);
#endif
}
通常使他们可以做这样的事情:
#define DEBUG(MSG) printf("[%s:%i] %s\n", __FILE__, __LINE__, (MSG))
由于在日志中有确切的调试消息源是非常有用的,这是一个很常见的模式。但是,如果你使用的功能,像这样:
void DEBUG(const char *MSG) {
printf("[%s:%i] %s\n", __FILE__, __LINE__, (MSG));
}
然后你只永远能看到相应的文件名和行号到printf()
呼叫DEBUG()
,从来没有那些所谓的DEBUG()
代码。
1)你的代码将打破,如果msg
为%d
或如此,因为预计的printf格式字符串。 printf("%s", msg);
更好。
2)不是真的。除非您是微型优化(例如代码大小),否则宏将被过度使用。函数更容易调试,因为您可以在调试器中执行诸如stop in debug
之类的操作。有很多其他的东西对宏来说很棘手。见http://www.brainbell.com/tutors/c/Advice_and_Warnings_for_C/Macros_and_Miscellaneous_Pitfalls.html
3)@Jonathan Grynspan指出 - 宏观形式更容易FILE,LINE使用。在我看来,开发人员喜欢在打字时使用快捷方式,这使得他们的代码难以在以后保持其他人的身份,并且后来更难调试。 IMO最佳实践:类型多,使你的代码易于调试,易于在调试器中运行,并使用函数签名debug(const char* msg, const char* FILE_LOC, unsigned LINE_NUMBER)
如果使用宏,则更改单个#define语句或编译选项并重新编译可使所有调试代码从可执行文件中消失。相反,如果一个使用DEBUG()
功能,则每次调用将生成代码来调用该函数,本身是否将功能做任何事情。
会吗?我假设编译器会优化函数调用,至少如果我将调试函数保存在与实际代码相同的文件对象中。 – Erik
一个好的编译器会优化掉空函数的调用。但是,编译器不能完全消除该功能;如声明的那样,它有外部作用域,所以编译器必须将它包含在目标文件中,以防其他模块的某些代码调用它。 (一个好的链接器可能会在以后消除它。)而且,如果它在多个源模块中定义,链接器将会抱怨多个定义。声明函数“static”允许编译器消除定义。 –
除了使用__FILE__
,__LINE__
,你也应该比较如下:
#ifdef NDEBUG
#define DEBUG_PRINT(...) ((void)0)
#else
#define DEBUG_PRINT(...) printf(__VA_ARGS__)
#endif
反对你的函数:
void debug(const char* msg) {
#ifndef NDEBUG
printf("%s", msg);
#endif
}
随着宏观,我可以这样写:
DEBUG_PRINT("Expected %d, got %d\n", correct_value, result);
用的功能,我必须去一些努力构造使用我的整数使用一个或多个字符串,并调用该函数一次或多次。在发布模式下,该函数什么也不做,所以字符串未被使用。优化可能管理,以消除代码来构建它,或然后再它可能不是。毫无疑问,宏观。
这就是说,你可以写你的debug
功能,做正确的事与可变参数。但是你写的函数最终会遇到这个问题,你必须添加一个debugf
。
我已修复我的代码,因为该错误与问题无关。 – Erik
这些不是强大的参数,用于将简单的调用包装到另一个函数中的'printf'中。鉴于这个问题特别是关于printf式的调试,我不认为调试器的使用是相关的。 – chepner