C语言之assert()

来源:互联网 发布:中科院半导体所 知乎 编辑:程序博客网 时间:2024/06/11 13:51

转载地址:http://blog.chinaunix.net/uid-21736802-id-1817877.html

在调试程序时,经常会用到assert和printf之类的函数,我最近做的这个工程里就有几百个assert,在你自认为程序已经没有bug的时候,就要除去这些调试代码,应为系统在正常运行时这些用于调试的信息是无用的,而且会占用时间和空间。怎么删除呢,俺以前都是用笨方法,一个一个注释,能用注释也是经过改进的方法,俺最早都是删掉之后出了问题再重新写的,但是这次几百个一个一个删除的话可是要了俺的小命了,一首mp3听完,还不到一百个。以前看过st的函数库,老外的代码就是规范,俺现在的代码好多都是在st和ti那里照搬的,呵呵。
下面给出最简单的一种方法:

#define DEBUG#ifdef DEBUG#define PRINTF(x) printf x#else#define PRINTF(x) ((void)0)#endif

使用时,PRINTF(( “Hello World!\n\r” ));
注意这里是两个括号,一个会报错的
不使用时,直接将”#define DEBUG”屏蔽掉
另外一个调试时常用的方法是assert,还是在一个头文件里,这里用的是STM32函数库的例子

#ifdef DEBUG 1/******************************************************************************* Macro Name : assert_param* Description : The assert_param macro is used for function's parameters check.* It is used only if the library is compiled in DEBUG mode.* Input : - expr: If expr is false, it calls assert_failed function* which reports the name of the source file and the source* line number of the call that failed.* If expr is true, it returns no value.* Return : None******************************************************************************#define assert_param(expr) ((expr) ? (void)0 : assert_failed((u8 *)__FILE__, __LINE__))/* Exported functions ------------------------------------------------------- */void assert_failed(u8* file, u32 line);#else#define assert_param(expr) ((void)0)#endif/* DEBUG *///assert_failed此函数要自己定义#ifdef DEBUG/************************************************************************* Function Name : assert_failed* Description : Reports the name of the source file and the source line number* where the assert_param error has occurred.* Input : - file: pointer to the source file name* - line: assert_param error line source number* Output : None* Return : None******************************************************************************void assert_failed(u8* file, u32 line){/* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* Infinite loop */while (1){}}#endif

调试程序尤其是数据结构函数调用经常进行断言是一种好的习惯。本文简单介绍一下断言的使用方法,很简单Come on~
assert宏的原型定义在

#include <assert.h>void assert( int expression );

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

#include <stdio.h>#include <assert.h>#include <stdlib.h>int main( void ){        FILE *fp;        fp = fopen( "test.txt", "w" );//以可写的方式打开一个文件,如果不存在就创建一个同名文件        assert( fp );                            //所以这里不会出错        fclose( fp );        fp = fopen( "noexitfile.txt", "r" );//以只读的方式打开一个文件,如果不存在就打开文件失败        assert( fp );                            //所以这里出错        fclose( fp );                            //程序永远都执行不到这里来        return 0;}[root@localhost error_process]# gcc badptr.c [root@localhost error_process]# ./a.out a.out: badptr.c:14: main: Assertion `fp' failed.

已放弃
使用assert的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。
在调试结束后,可以通过在包含#include

#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不能代替条件过滤

assert和if的区别:首先,这两个处于c语言中的不同等级,assert不过是系统提供的一个函数,而if则是关键字。其次,两个的语法也不一样,你可以写if(1){}但是写个assert(1){}却不对,当然,这个区别是第一点造成的在语义上,if就不用解释了吧。

assert的语义如下:在debug编译模式下,如果assert的表达式求值为false,就会中断程序;在release模式下,则没有任何操作。因此可以说,assert就是在调试模式下用来确保制定条件被满足的一种手法,比如说,你想确保a>0,你就可以写assert(a>0);如果在运行过程中,a<=0了,代码就会中断,利用调试器很容易发现问题所在。

在“应用程序调试”一书里,作者强烈推荐使用assert,他的代码让同事都抱怨assert太多了。。。。。。

assert()

C语言的assert(),中文翻译为断言,一旦触发,带调试信息的程序就会立刻退出并且给出错误信息,这一点是俺这样的IT民工都知道的,因此很多人对assert()是又爱又恨,爱她因为她能时不时给出点错误信息,恨她因为不去掉的话老是在你不希望她出现的时候蹦出来。
其实,我觉得对于程序而言,assert()的做法是一种勇敢的人生态度,面对不可逆转的错误,究竟是一错再错的执行下去,还是拿出一点舍身取义的勇气,终止自己继续执行的权力?assert()选择了后者。
但有些情况下不应该使用assert(),而应该利用其他方式来检测。一切都得视情况而定,例如某些函数的返回值检查:
ptr = malloc(1024);
assert(ptr);
采用assert()来做这样的判断是不明智的,但如果在一个接口定义良好的函数内使用assert()却可以保证程序能够正确的履行接口所定义的任务。例如:

void enter_without_lock(struct something_t * foo){assert(NOT_HELD_MUTEX(foo->mutex));........}

因此如果你还在写程序,尤其是需要建立一个复杂的framework时,拿出点勇气来,多放几个assert()到这样的关键位置,不要轻易的去掉它,调试调试再调试,直到所有的assert()都不能正常通过,再发布你的release版本。