带参宏的“文本替换”(今天的一个错误总结)

先看今天我写的一段代码:

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.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值