先看今天我写的一段代码:
debug.h
/* debug.h */
void CDECL DebugMsgBox(const TCHAR *szFormat, ...);
debug.c(可以忽略函数实现部分, 只要看到我使用了可变参数列表就行了)
/* debug.c */
void CDECL DebugMsgBox(const TCHAR *szFormat, ...)
{
va_list pArgs;
TCHAR szBuf[BUF_MAX_LEN];
ZeroMemory(szBuf, sizeof(szBuf));
va_start(pArgs, szFormat);
_vsntprintf(szBuf, sizeof(szBuf) / sizeof(TCHAR), szFormat, pArgs); /* not _vsnprintf */
va_end(pArgs);
MessageBox(NULL, szBuf, TEXT("Debug message:"), MB_OK);
}
这里我实现了一个函数(实际上是重用了《Windows程序设计》一书上的某个程序)用来输出调试信息, 但是我既想利用可变参数的便利, 又想通过宏来控制DebugMsgBox的行为, 于是我又在debug.h中加了如下代码:
/* debug.h */
#define DEBUG_MODE
#ifdef DEBUG_MODE
#define DEBUG_MSG(list) DebugMsgBox(list)
#else
#define DEBUG_MSG(list) ((void)0)
#endif
(当然, 这样定义宏在VC++6.0下会产生一个警告:warning C4002: too many actual parameters for macro 'DEBUG_MSG'暂且不提)
然后我在输出调试信息时通过使用:
DEBUG_MSG(TEXT("%s"), TEXT("Hello world!"))
来代替:
DebugMsgBox(TEXT("%s"), TEXT("Hello world!"))
其实我是想通过 DEBUG_MODE来控制DEBUG_MSG的行为:当我确实需要调试时, 我希望DEBUG_MSG(TEXT("%s"), TEXT("Hello world!"))相当于函数调用DebugMsgBox(TEXT("%s"), TEXT("Hello world!")),而不需要调试时, 我希望DEBUG_MSG(TEXT("%s"), TEXT("Hello world!"))相当于一个毫无作用的代码行((void)0), 以减小运行时开销。
但是, 我的目的能达到吗?
不能。
虽然宏是简单的文本替换, 但是对于带参数的宏, 它是严格按照参数个数来替换的。 看下面这段程序:
#include <stdio.h>
#define PRINT(list) printf(list)
int main(void)
{
printf("Hello %d %s\n", 100, "world");
PRINT("Hello %d %s\n", 100, "world");
printf("Hello %d %s\n");
return 0;
}
控制台上会输出什么样的结果呢?
Hello 100 world
Hello 0 (null)
Hello 0 (null)
Press any key to continue
由此可见, 对于只带一个参数的宏, 当你传递给它两个参数时, 只有第一个参数被正确宏替换, 而第二个参数就被丢弃了。
希望别的小童鞋不要犯和我一样的低级错误啊。
C99中倒是有参数个数可变的宏。
PS: 这是俺第一次看到printf输出null。
2011-10-28:
zotin大哥的评论:
“
其实还是有一种很不漂亮的解决办法。
#define DEBUG_MSG(list) DebugMsgBox list
这儿的list不要用括号
使用宏的时候:
DEBUG_MSG( (TEXT("%s"), TEXT("Hello world!")) )
这儿多一层括号,就能展开成你想要的形式。
虽然多一层括号看起来很怪异,但至少能按想要的方式工作。
”
2011-11-08日07ware的评论:
#ifdef _MSC_VER
//Microsoft VC++
#define TRACE(fmt, ...) DebugMsgBox("%s, %s [Line %d]: " fmt, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
#else
//GCC on Linux & MacOS
#define TRACE(fmt, ...) DebugMsgBox("%s, %s [Line %d]: " fmt, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__)
#endif
Maybe VC6.0 is too old to support VA_ARGS, anyway all morden C++ compilers support my solution ;-)
Also, there is a simple optimization in your code:
TCHAR szBuf[BUF_MAX_LEN];
ZeroMemory(szBuf, sizeof(szBuf));
==>
TCHAR szBuf[BUF_MAX_LEN] ={0};
no need to call Windows API to zero a buffer.