了解断言
一、看一看断言实现:
在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 NDEBUG ,assert就Do 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为一个特殊的变量,简单的说它含有定义它的函数名称(具体解释在头文件注释中有详细说明)。
你也可以写一个简单程序看一下这几个变量:
运行结果如下:
这几个特殊的变量传给__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时,启用命名空间,这样就更容易控制这个宏了。



浙公网安备 33010602011771号