/***
*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_fal

 

看了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!!不过,能折腾是福气。

我自己是个小小菜鸟,刚上路,必然有很多解释是有问题的,欢迎大家指正,共同交流学习提高!