assert与嵌入式c语言调试开关

在调试程序时,经常会用到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
void assert( int expression );
assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
请看下面的程序清单badptr.c:
#include
#include
#include
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 的语句之前插入 #define NDEBUG 来禁用assert调用,示例代码如下:
#include
#define NDEBUG
#include
用法总结与注意事项:
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版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值