c语言中的assert

assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
#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 <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不能代替条件过滤

[附]ASSERT也许是一个很好的东西,断言一个必须成立的值。

但是我从来没有使用过,而是一直使用“老套”的返回值的方法,原因有以下几点:

1.   ASSERT不能保证release下不出问题。

一个程序,里面可能会有各种各样的错误,一个十全十美的应用程序,是不可能的,毕竟人不是机器。这个时候,微软就提供了一个叫ASSERT的宏来判断一个值是否为假,并且在MFC源代码里面大肆狂用。这个宏很不错,让大家在DEBUG下能判断出假值。但是release下就判断不了了,当然你可以使用assert()函数,不过我们只讨论ASSERT宏。

有人说,只要DEBUG下断言成功了,RELEASE下就不会出错了。我们仅仅是希望如此而已。举一个例证明一下,内存溢出是所有会写代码的人都会遇到过的问题。你断言成功了,并不代表你内存不会溢出。这时,内存被踩了,谁都无法辨认,当然ASSERT它也不能。这个时候你的程序会继续向下执行,可能在很久之后才会出现错误。这无疑对你查找溢出点增加了很多难度。

如果你不是断言,而是使用的错误判断,很可能就在那一行代码就是查出错误,因为断言只能判断真假,而你的错误判断可以五花八门千变万化,并以错误码返回。当然可能有朋友会说这样会浪费性能,是,不过我更愿意些。

如果要继续举例,可以举出很多来。

2.   使用ASSERT提供的接口,我向来认为不好。

为什么要写这篇讨论,是因为我在网上看到了一们兄台的BLOG,其中有对ASSERT的讨论。http://www.cppblog.com/darkdestiny/archive/2006/07/15/10101.aspx其中有一段话让我想写这篇贴子。“因为我们写函数(接口)的目的,是希望能被正确的调用,而不是胡乱(导致局部或者整体状态混乱)的使用(为此我们居然要将assert写成错误处理的形式,以容忍各种白痴的调用行为).”

我不这样认为,我认为一个接口的提供者应该考虑到各种情况并且能够适应各种情况,这样的接口才是一个优秀的接口,也就是说随便你怎么折磨它都行。当然一个优秀的程序员,是应该仔细按照文档来编写代码,但是谁也不想一运行程序就“当”的一下弹出来错误吧。如果是MFC,至少还有代码可供你参考,你的接口如果没有代码,那别人很难知道是什么错误,因为ASSERT错误不会告诉什么值错了。更不用说RELASE版了,程序运行后会发生什么事,没人能知道。

这个时候如果你有错误返回值,那这一切都完美的解决了。开发人员可以一看返回值就知道是出了什么错误,这样多好,至少我这样觉得。


3.   目前看来,没有什么程序是必须必须要执行下去,而不能返回的。

ASSERT的作用就是在DEBUG里面帮你判断,RELASE里面不管你,也就是在DEBUG里面,值一定要正确,然后执行下去,RELEASE里面无论有不有值程序都将执行下去。我想,应该不会有什么函数是要求不管有不有错误都必须执行下去吧。难道就不能给出一个返回值来告诉作者哪里出错了吗?

在此我置疑MFC里面那大篇大篇的ASSERT。


4.   上面的都是我的一些看法和观点,可以比较狭隘和偏执。希望大家热烈讨论,如果觉得我不对,请说出使用它的理由来,并举例说明,谢谢。我也很希望知道是我自己错了,一般还是不要怀疑微软的东西比较好。



转自:http://gubing2005.blog.163.com/blog/static/68536532201062002332774/

你可能感兴趣的:(c语言中的assert)