assert宏的深入学习

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

一,assert宏的定义

如下:

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


__assert_fail函数是一个库导出函数,导出定义如下:

/* 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__));
注意上面的__attribute__((__noreturn__))定义,这个属性通知编译器,该函数从不返回。运行完这个函数程序就退出来了。

二,禁用assert宏


这里要注意一个宏,这个宏很有用,可以禁用assert宏,是如何做到的呢?

#ifdef  NDEBUG

# define assert(expr)           (__ASSERT_VOID_CAST (0))

/* void assert_perror (int errnum);

   If NDEBUG is defined, do nothing.  If not, and ERRNUM is not zero, print an
   error message with the error text for ERRNUM and abort.
   (This is a GNU extension.) */

# ifdef __USE_GNU
#  define assert_perror(errnum) (__ASSERT_VOID_CAST (0))
# endif

#else /* Not NDEBUG.  */
所以,就是如果我们自己定义了NDEBUG宏的话,assert就不会工作了,因为__ASSERT_VOID_CAST宏定义如下:

#if defined __cplusplus && __GNUC_PREREQ (2,95)
# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif

三,assert宏注意事项

  1. 最好使用assert宏检查一个条件,就是不要用&&或者||操作符,这样更容易发现是哪个条件出现问题,在需要的时候,多写几个assert宏。
  2. 不要使用assert进行变量修改,如assert(k++>10),因为我们可能会禁用这个宏,此时,k++是不会执行的,正如上面我们看到的一样。
  3. assert不是条件过滤。
四,自定义实现ASSERT宏
我们自己可以定义实现ASSERT宏,这样可以得到更多的错误信息,也可以自己定义错误行为。
#ifndef ASSERT
#define ASSERT(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) {
    Log_Assert(function, file, line, expression);
    Break();
    return false;
  }
  return true;
}
上面的Log_Assert函数只用于输出错误信息。
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的库函数,可以对进程附加调试信息,还可以加断点什么的,其实就是我们有时会遇到的问我们是否要调试,如果我们选择是的话,就会启动一个开发环境,打开调试器。
最好的话,实现ASSERT时,启用命名空间,这样就更容易控制这个宏了。

你可能感兴趣的:(assert宏的深入学习)