似乎对于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()
in 调用对应的文件名和行号DEBUG()
,而永远不会看到调用DEBUG()
.
如果使用DEBUG
宏,则更改单个#define 语句或编译选项并重新编译可以使所有调试代码从可执行文件中消失。相比之下,如果一个人使用一个DEBUG()
函数,那么每次调用都会生成调用该函数的代码,而不管该函数本身是否做任何事情。
msg
1)如果是这样的话,你的代码会中断%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)
除了使用__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
.