assert宏主要是在程序调试版本中使用,这里总结了关于assert和assert_perror的使用方法和一些关键问题和考虑。
assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
# define assert(expr) \
((expr) \
? __ASSERT_VOID_CAST (0) \
: __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
b) 当NDEBUG宏定义使能时,assert宏并不起实质性效果,等同于没有任何执行代码:
# define assert(expr) (__ASSERT_VOID_CAST (0))
注:NDEBUG宏定义必须在 #include <assert.h>之前使能。
注:assert.h头文件在路径/usr/include/下可以找到。
2) 当__USE_GNU宏定义使能时,在<assert.h>中的assert_perror宏原型定义:
assert_perror的作用是根据errnum向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
# define assert_perror(errnum) \
(!(errnum) \
? __ASSERT_VOID_CAST (0) \
: __assert_perror_fail ((errnum), __FILE__, __LINE__, __ASSERT_FUNCTION))
b) 当NDEBUG宏定义使能时,assert_perror宏并不起实质性效果,等同于没有任何执行代码:
# define assert_perror(errnum) (__ASSERT_VOID_CAST (0))
注:errnum定义在errno.h中给出,在Linux下错误代码定义分别可以如下方式定位得到:
/usr/include/errno.h
--> /usr/include/bits/errno.h
--> /usr/include/linux/errno.h
--> /usr/include/asm/errno.h
--> /usr/include/asm-generic/errno.h 错误码:35~131
--> /usr/include/asm-generic/errno-base.h 错误码:1~34
注:在/usr/include/asm-generic/errno-base.h中前面几个错误码定义如下,其中错误码1为 Operation not permitted,后续测试代码将会用到这个错误码:
assert调试函数考虑的几个问题:
2) 当assert的函数作用时,频繁的调用会极大的影响程序的性能,增加额外的开销。
3) 模块内部开发设计是严谨的,代码逻辑不会导致严重的函数参数非法异常。
4) 从模块设计角度出发,模块开发人员对模块异常逻辑处理考虑是完善的。
5) 模块入口和出口对数据一致性和合法性进行检查,模块对外异常的屏蔽能力。
6) 测试用例覆盖对外接口异常和模块逻辑异常。对外接口测试尽量保证最大的测试覆盖率。
7) 在调试结束后,可以通过在包含#include <assert.h>的语句之前插入 #define NDEBUG 来禁用assert调用,屏蔽频繁调用带来的性能影响。
这里给出一个简单用例,给出如何使用assert和assert _perror函数的使用方法:
void test_assert(void *p)
{
assert(NULL != p);
printf("%s, %d, %s -> [p = 0x%x].\r\n",
__FILE__, __LINE__, __func__, (unsigned int)p);
}
void test_assert_perror(int err)
{
assert_perror(err);
printf("%s, %d, %s -> [p = 0x%x].\r\n",
__FILE__, __LINE__, __func__, err);
}
- debug版本给出的测试结果:
- release版本给出的测试结果:
demo测试main函数代码(0:测试test_assert; 1:测试test_assert_perror):
int main(int argc, char *argv[])
{
int i;
if (2 != argc)
{
Help(argv[0]);
return -1;
}
i = atoi(argv[1]);
switch(i)
{
case 0:
test_assert(1);
test_assert(NULL);
break;
case 1:
test_assert_perror(0);
test_assert_perror(1);
break;
default:
Help(argv[0]);
return -2;
}
return 0;
}
assert调试函数用法总结与注意事项[1]:
int resetBufferSize(int nNewSize)
{
//功能:改变缓冲区大小,
//参数:nNewSize 缓冲区新长度
//返回值:缓冲区当前长度
//说明:保持原信息内容不变 nNewSize<=0表示清除缓冲区
assert(nNewSize >= 0);
assert(nNewSize <= MAX_BUFFER_SIZE);
...
}
2) 每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败
Bad: assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);
Good: assert(nOffset >= 0);
Good:assert(nOffset+nSize <= m_nInfomationSize);
3) 不能使用改变环境的语句,因为assert仅作用于DEBUG版本。(如果这么做,会使用程序在真正运行时遇到问题。)
Wrong: assert(i++ < 100) 这是因为如果出错,比如在执行之前i=100,那么这条语句就不会执行,那么i++这条命令就没有执行。
Correct: assert(i < 100)
i++;
4) assert和后面的语句应空一行,以形成逻辑和视觉上的一致感。
5) 有的地方,assert不能代替条件过滤,因为assert仅作用于DEBUG版本。(如果这么做,会使用程序在真正运行时遇到问题。)
参考文档:
【1】assert用法总结