assert是大家常用的宏,它的用法相信大家都有所了解。P.J Plauger的"The C Standard Library"一书中提到在源代码中切换assert宏定义的方法:
/* turn assertion on */
#undef NDEBUG
#include
/* turn assertions off */
#define NDEBUG
#include
我顺手写了一个例子如下:
/* testmacro1.c */
#define NDEBUG
#include
int main() {
assert(0); // => ((void)0);
#undef NDEBUG
#include
assert(0); // => (void)((0) || (__assert("0", "testmacro.c", 10), 0));
}
测试结果正如P.J Plauger的说明。但仔细看来似乎有些疑惑:总觉得第二个assert也应该展开成((void)0)才对啊。由于NDEBUG被定义,在第一次assert.h展开时,assert就被替换成了((void)0),而后虽然NDEBUG被disable了,但此时由于assert.h的header file guard保护,assert的新定义并没有被重新loaded & evaluated,所以assert似乎依然应该被展开为((void)0),但执行结果却不是。
我自己写了一个程序测试了一下:
/* testmacro1.h */
#ifndef TEST_MACRO1_H
#define TEST_MACRO1_H
#ifdef X_DEBUG
#define x_debug(expr) ((void)0)
#else
#define x_debug(expr) #expr
#endif
#endif
/* testmacro1.c */
#define X_DEBUG
#include "testmacro1.h"
int main() {
x_debug(0); // => ((void)0);
#undef X_DEBUG
#include "testmacro1.h"
x_debug(0); // => ((void)0)
}
果不其然,结果正如我所猜测的:
#undef X_DEBUG
#include "testmacro1.h"
并没有改变x_debug的定义,那么第一个例子到底是怎么回事呢?
其实这是C标准库设计所致,打开你所在系统的assert.h标准文件,我在sun solairs 9上是这样的:
#ifndef _ASSERT_H
#define _ASSERT_H
… …
#endif
#undef assert
#ifdef NDEBUG
#define assert(EX) ((void)0)
#else
#define assert(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
#endif /* NDEBUG */
哈哈,这下子看清楚了,原来assert的定义根本不在Header File Guards的保护下,怪不得我思前想后都对不上呢:),因为没有File Guards的保护。使头文件中的宏有机会被重新loaded&envaluated。
下面例子中的第三个assert屏蔽掉了标准库中的assert实现:
/* testmacro3.c */
#define NDEBUG
#include
int main() {
assert(0); // => ((void)0);
#undef NDEBUG
#include
assert(0); // => (void)((0) || (__assert("0", "testmacro3.c", 10), 0));
#define assert(exp) (#exp)
assert(x==0); // => ("x==0");
}
这种屏蔽很简单,就不多说了,自己看吧。
评论