先看今天我寫的一段代碼:
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.