/***
*assert.h - define the assert macro****/#include <crtdefs.h>//移除可能的assert定义,确保assert未定义
//但据测试,“后面”的#defines会覆盖之前的#defines
//这里考虑周全,值得学习!
#undef assert//如果定义了NDEBUG,那么关闭assert宏
//在Release模式下,会自动定义NDEBUG,这样就取消了assert断言,即定义为一个no-op
#ifdef NDEBUG#define assert(_Expression) ((void)0)
//为什么要定义为((void)0),而不是“一片空白”。解释如下:
// 1.((void)0)是一个完整的表达式,但是“空白”不是
// 2.如“assert(1==2), 12;”是一个表达式,如果将assert(...)定义为“空白”,将无法通过编译
// 3.((void)0)是一个no-op的表达式,什么都不做,编译器将忽略它们(应该不会为它生成代码)
// 4.其实说白了就是shut up the compiler,让编译器闭嘴!
#else //没有定义NDEBUG,即在Debug模式下,将会定义assert为特殊的调试函数#ifdef __cplusplus //如果是cpp环境,需要注意对c函数的链接方式!
extern "C" {#endif_CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message,_In_z_ const wchar_t *_File, _In_ unsigned _Line);//_In_z - nullterminated 'in' parameters.
//_In_ - input (pointer) parameter
//_CRTIMP - “空白”或者如果定义_DLL的话,替换为__declspec(dllimport)
#ifdef __cplusplus}#endif//为了阅读方便,做了换行处理
#define assert(_Expression) \(void)( (!!(_Expression)) || \
(\_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), \0\) \)//||的短路!
//如果!!(_Expression)结果是真,那么后面的代码根本就不会执行,如果是假,那么执行!
//返回值适配器:
//_wassert函数是void,怎么能应用于||呢!,好办:加个“,0”即可,这样整个表达式返回值就是0!
//但是assert不应该返回任何值,再将整个表达式结果转成void,是整个表达式,最外层的括号要注意!
//宏中字符串连接的方式:##(Token-Pasting Operator )和#(stringizing operator)
//_CRT_WIDE(#_Expression) - L ## _Expression (Long型的字符串,即2个字节存储之)
//__FILE__和__LINE__
//"!!"的妙处
//_w开头,是UNICODE版本,当然也就存在另外一个_assert版本的函数,
//跟踪_wassert定义,发现它其实暗地里做了不少的事情,有关stderr和缓冲区各种处理等,
//库本身也给出了注释:
/*
* Build the assertion message, then write it out. The exact form* depends on whether it is to be written out via stderr or the* MessageBox API.*///在VC中,assert如果未通过,会输出如下格式的提示:
// Assertion failed: <expr>, file <filename>, line <lineno>
//并且,最后会给出一个消息对话框,让用户选择
//if (nCode == IDABORT)
//{ raise(SIGABRT);
// _exit(3);}
//if (nCode == IDRETRY)
//{ __debugbreak();
// return;}
//if (nCode == IDIGNORE)
// return;
//需要注意的是,在_wassert函数最后一行是abort()函数调用!
// exit():温和!
// abort():暴力!直接关闭当前的process,文件资源什么的不管!
#endif /* NDEBUG */
下面是assert的MessageBox的显示,具体的代码可以定位到assert.c中查看。
看了assert.h的源码后,确实收获不少,其实这是次很偶然的阅读,本来是深入了解下VC++2010的Debug功能的,就想到了assert。
总结下:条件编译、链接方式,宏的使用,返回值隐瞒,assert的内在行为……
下面的更简单的测试代码完全是小小case了,当然,它本身没有多大意义。
得提下,在《Writing solid code》中,对assert的应用语义解释很详细也深入,书比较老,但意义犹大。看过,但忘得似乎差不多了。。。
void output(const char* msg){cout << msg << endl;}#define test(exp) (void) ((!!(exp)) || (output("hello"), 0))
当然,VC的源码不是copyleft的,还是得声明下,只是学习之用。最后要抱怨下:那么多#defines!!不过,能折腾是福气。
我自己是个小小菜鸟,刚上路,必然有很多解释是有问题的,欢迎大家指正,共同交流学习提高!