c++ assert()学习总结

ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。

assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
#include <assert.h>
void assert( int expression );

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

使用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)的使用,方法很简单。为什么要用,初学者可能比较迷惑。   
契约式编程讲的比较清楚,建议可以先看看这类书。   
一个函数由前置条件、后置条件和不变式组成。在VC中,我们可以通过断言来保证这三个条件。可以大大提高了软件的质量。   
---------------------------------------------------------------   

如果ASSERT()中的条件不成立(比如ASSERT(0);),会弹出一个比较吓人的对话框。
点击重试,可以到达ASSERT断言不成立的那一行,此时可以在watch窗口查看变量值,找出出错的原因。


提高程序健壮性之assert使用

编写能正常运行的程序很难;编写在错误情况下仍然表现的很“优雅”的程序更难。这篇文章将和大家讨论一些编程技巧,可以使我们在运行中的程序中早点发现错误,检测和从问题中恢复。那就先讨论下断言(assert)的使用吧。

在编码时,有一个好的目标应该时刻铭记在心,那就是:应该想办法让bug或者异常错误尽早使得程序down掉,或者出现错误。因为这样可以帮助你在开发和测试阶段尽快找出bug。有一些错误不会无缘无故的暴露自己,往往是产品都到了客户手上,这些错误才会显现出来。

一个最简单的检查异常条件的方法是使用标准C的assert宏,它的参数是一个bool表达式。当表达式为假时,程序会退出。在退出之前打印错误消息,包括源文件,行号,和表达式本身。断言非常有用,它提供了一个作用于程序内部的广泛的一致性检查方法。例如,使用断言测试函数参数的有效性,测试异常的返回值等等。

每一个断言的使用不仅提供了一个程序运行时的条件检查,也像一个对源代码级别的程序操作的说明性文档。如果你的程序包含了一个断言,也就是告诉那些阅读你源代码的人,在你的源代码中,在程序的这一点,这个条件应该为真,如果不为真,那就是一个bug。

当然,在追求性能的代码中,使用assert会降低程序性能。但是你放心,在编译时加入NDEBUG参数编译器就可以对assert进行预处理,从而移除它。正因为在预处理时可能移除assert,那你使用时就得小心了。什么时候用,什么时候不用就成了一个问题。通常,你不应该在assert内部调用函数,定义变量,或者使用改变值的操作符,如++。

我们假设你这样使用了:

for (i = 0; i<= 100; ++i) assert (do_something () == 0);

然后,你可能会发现这样会使得性能大大降低,从而在重新编译时使用NDEGUG参数。这将移除整个assert宏,这就将do_something( )也被移除了,再也不被调用。为了纠正错误,你应该这样写:

for (i = 0; i &lt;= 100; ++i) { int status = do_something (); assert (status == 0); }

另外应该铭记在心的是,不要用assert去检查无效的输入。用户可不喜欢自己在输入时程序直接退出,即便是输入错误,程序最好也有友好的响应。所以,你应该对无效输入进行检查,并输出一些有用的提示信息。只在程序运行中进行内部检查时使用断言。

在这里,我会给出一些比较好的在程序中使用assert的地方:

(1)空指针检查。例如,针对一个函数的参数进行空指针检查。你可以这样使用:assert (pointer != NULL);,产生的错误会像这样:Assertion ‘pointer != ((void *)0)’ failed。这样,当出现空指针时,你的程序就会退出,并很好的给出错误信息。

(2)检查函数参数的值。例如,如果一个函数只能在它的一个参数foo为正值的时候被调用,你可以在函数开始时这样写:assert (foo > 0);,这将帮助你检测函数的错误使用,这也给源代码阅读者很清晰的印象,那就是在这里对函数的参数值有限制。

说了这么多,行动起来吧,大胆的在你的程序中使用断言。



发布了57 篇原创文章 · 获赞 29 · 访问量 17万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章