assert宏简明使用指南

assert宏主要是在程序调试版本中使用,这里总结了关于assert和assert_perror的使用方法和一些关键问题和考虑。


1) 在<assert.h>中的assert宏原型定义:

assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。


a) 当NDEBUG宏定义未使能时,assert宏主要用于判断条件是否满足程序终止:
# 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 来终止程序运行。


a) 当NDEBUG宏定义未使能时,assert_perror宏主要用于向stderr打印一条出错信息:
#  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调试函数考虑的几个问题:


1) assert函数主要用于调试阶段,常在Debug版本中使用。
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]:


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用法总结



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值