/****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!!不过,能折腾是福气。
我自己是个小小菜鸟,刚上路,必然有很多解释是有问题的,欢迎大家指正,共同交流学习提高!
