assert宏的深入学习
assert宏的原型定义在头文件assert.h中,它的作用是如果宏后面的条件返回假,则终止程序的执行,该宏会调用__assert_fail函数,这个函数内部会先向stderr输出错误信息,然后调用abort函数来终止程序的执行。
一,assert宏的定义
如下:
[cpp]
# define assert(expr) \
((expr) \
? __ASSERT_VOID_CAST (0) \
: __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
__assert_fail函数是一个库导出函数,导出定义如下:
[cpp]
/* 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宏,是如何做到的呢?
[cpp]
#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宏定义如下:
[cpp]
#if defined __cplusplus && __GNUC_PREREQ (2,95)
# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif
三,assert宏注意事项
最好使用assert宏检查一个条件,就是不要用&&或者||操作符,这样更容易发现是哪个条件出现问题,在需要的时候,多写几个assert宏。
不要使用assert进行变量修改,如assert(k++>10),因为我们可能会禁用这个宏,此时,k++是不会执行的,正如上面我们看到的一样。
assert不是条件过滤。
四,自定义实现ASSERT宏
我们自己可以定义实现ASSERT宏,这样可以得到更多的错误信息,也可以自己定义错误行为。
[cpp]
#ifndef ASSERT
#define ASSERT(x) \
(void)Assert((x), __FUNCTION__, __FILE__, __LINE__, #x)
#endif
Assert实现如下:
[cpp]
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定义对错误处理的行为,该函数定义如下:
[cpp]
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时,启用命名空间,这样就更容易控制这个宏了。
补充:软件开发 , C++ ,