阅读源码(0)——VC中的assert.h,10多行代码两个宏,值得推敲推敲

/***

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

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

你可能感兴趣的:(assert)