了解断言

一、看一看断言实现:

  在Linux 中的/usr/include/assert.h中,我们可以看看assert宏的定义:

 

#ifdef    NDEBUG 
# define assert(expr)        (__ASSERT_VOID_CAST (0)) 
    ... 
#endif

 

#if defined __cplusplus && __GNUC_PREREQ (2,95)    
# define __ASSERT_VOID_CAST static_cast<void>    // 如果为c++ 
#else 
# define __ASSERT_VOID_CAST (void)               // 如果为c 
#endif

  当定义NDEBUG时,那么 assert(expr)实际为:static_cast<void>(0)(void)(0)
可以看出,不论是c还是c++,assert在NDEBUG时,Do nothing!

  那如果要禁用assert,很简单,在程序中 #define NDEBUGassertDo noting

  接着向下看:(为了简单起见,只讨论c语言)

# define assert(expr)                            \
  ((expr)                                \
   ? __ASSERT_VOID_CAST (0)                        \
   : __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))

  当expr为真时,它执行__ASSERT_VOID_CAST(0),也是相当于什么也不做嘛!
  如果expr为假,则执行: __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
我们层层展开来:

 /* This prints an "Assertion failed" message and aborts.  */
extern void __assert_fail (__const char *__assertion, __const char *__file,
               unsigned int __line, __const char *__function)
     __THROW __attribute__ ((__noreturn__));

其中:__STRING()会把expr转换成字符串;__FILE__为.c或.cpp文件文件名称;__LINE__为代码所在的行数;__ASSERT_FUNCTION为一个特殊的变量,简单的说它含有定义它的函数名称(具体解释在头文件注释中有详细说明)。
你也可以写一个简单程序看一下这几个变量:

Unnamed QQ Screenshot20131227182522

 
运行结果如下:

Unnamed QQ Screenshot20131227182549
 

  这几个特殊的变量传给__assert_fail函数,此函数为一个库导出函数,注释中描述这个函数作用是向错误输出中打印"Assertion failed"信息,然后调用abort函数来终止程序执行。

assert宏的原型定义在头文件assert.h中,它的作用是如果宏后面的条件返回假,则终止程序的执行,该宏会调用__assert_fail函数,这个函数内部会先向stderr输出错误信息,然后调用abort函数来终止程序的执行。其中__attribute__((__noreturn__))属性通知编译器,该函数从不返回。

二,assert宏注意事项

  1.最好使用assert宏检查一个条件,就是不要用&&或者||操作符,这样更容易发现是哪个条件出现问题,在需要的时候,多写几个assert宏。
  2.不要使用assert进行变量修改,如assert(k++>10),因为我们可能会禁用这个宏,此时,k++是不会执行的,正如上面我们看到的一样。
  3.assert不是条件过滤。

三,自定义实现ASSERT宏


  我们自己可以定义实现ASSERT宏,这样可以得到更多的错误信息,也可以自己定义错误行为。
#ifndef MYASSERT
#define MYASSERT(x) \
    (void)Assert((x), __FUNCTION__, __FILE__, __LINE__, #x)
#endif
Assert实现如下:

inline bool Assert(bool result, const char* function, const char* file,
                   int line, const char* expression) {
  if (!result) { 
    assertFunc(function, file, line, expression);//你可以打印到错误输出或者写入log

    Break();
    return false;
  }
  return true;
}
Break定义对错误处理的行为,该函数定义如下:

void Break() {
#if WIN32
  ::DebugBreak();
#elif OSX  // !WIN32
  ::Debugger();
#else // !OSX && !WIN32
#if _DEBUG_HAVE_BACKTRACE
  OutputTrace();
#endif
  abort();
#endif // !OSX && !WIN32
}

  DebugBreak是一个VC的库函数,可以对进程附加调试信息,还可以加断点什么的,其实就是我们有时会遇到的问我们是否要调试,如果我们选择是的话,就会启动一个开发环境,打开调试器。
  最好的话,实现MYASSERT时,启用命名空间,这样就更容易控制这个宏了。

posted @ 2013-12-27 18:38  NoodlesMoMo  阅读(173)  评论(0)    收藏  举报