在pjsip源码中有很多pj_assert的用法,它实际上就是c库assert宏。
#ifndef pj_assert
# define pj_assert(expr) assert(expr)
#endif
assert宏的原型定义在<assert.h>中 ,其作用是如果它的条件返回错误,则终止程序执行,linux下原型定义:
# define assert(expr) \
((expr) \
? __ASSERT_VOID_CAST (0) \
: __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
# else
其他系统定义:
# define assert(expr) \
((expr) \
? __ASSERT_VOID_CAST (0) \
: __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
#define ASSERT(f) \
do \
{ \
if (!(f) && AfxAssertFailedLine(THIS_FILE, __LINE__)) \
AfxDebugBreak(); \
} while (0) \
其作用是先计算表达式expr,为真,就把 0 转化为 void *,相当于什么都没做;
如果其值为假(即为0),执行 __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__));
即打印出assertion failed消息并aborts终止程序。也有定义如下:
void
__assert_fail (const char *assertion, const char *file, unsigned int line,
const char *function)
{
__assert_fail_base (_("%s%s%s:%u: %s%sAssertion `%s' failed.\n%n"),
assertion, file, line, function);
}
这里assert_fail_base函数会向stderr输出错误信息:
(void) __fxprintf (NULL, "%s", str);
(void) fflush (stderr);
那么它会打印出来assert的内容和__FILE__, __LINE__, __ASSERT_FUNCTION,即(文件名,行号,函数名),然后执行abort()函数使kernel杀掉自己并coredump(是否生成coredump文件,取决于系统配置);否则,assert()无任何作用。宏assert()一般用于确认程序的正常操作,其中表达式构造无错时才为真值。完成调试后,不必从源代码中删除assert()语句,因为宏NDEBUG有定义时,宏assert()的定义为空。
#ifdef NDEBUG
# define assert(expr) (__ASSERT_VOID_CAST (0))
assert只应该出现在debug版本中不应该出现在release版本中,Debug中可以用#undef NDEBUG启用assert,
在调试结束后,如Release版本中关闭assert函数,可以通过在包含#include <assert.h>的语句之前插入 #define NDEBUG 来禁用assert调用,示例代码如下:
#include <stdio.h>
#define NDEBUG
#include <assert.h>
用法总结与注意事项:
1)在函数开始处检验传入参数的合法性,如:
int resetBufferSize(int nNewSize)
{
//功能:改变缓冲区大小,
//参数:nNewSize缓冲区新长度
//返回值:缓冲区当前长度
//说明:保持原信息内容不变nNewSize<=0表示清除缓冲区
assert(nNewSize >= 0);
assert(nNewSize <= MAX_BUFFER_SIZE);
}
2)每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败
/***不好***/assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);
/****好****/assert(nOffset >= 0);
assert(nOffset+nSize <= m_nInfomationSize);
3)不能使用改变环境的语句,因为assert只在DEBUG生效,如果这么做,会使用程序在真正运行时遇到问题
assert(i++ < 100);//错误
//这是因为如果出错,比如在执行之前i=100,那么这条语句就不会执行,那么i++这条命令就没有执行。
//正确
assert(i < 100);
i++;
4)assert和后面的语句应空一行,以形成逻辑和视觉上的一致感
5)有的地方,assert不能代替条件过滤
//注意:当对于浮点数:
#include<assert.h>
float pi=3.14f;
assert (pi==3.14f);
//在switch语句中总是要有default子句来显示信息(Assert)。
int number = SomeMethod();
switch(number){
case 1: Trace.WriteLine("Case 1:");
break;
case 2: Trace.WriteLine("Case 2:");
break;
default : Debug.Assert(false);
break;
}