断言(Assert)使用、原理以及注意事项详解

本文介绍了assert在程序测试中的作用,如何通过NDEBUG和DEBUG宏控制其使用,提供了用法示例和注意事项,以及assert的原理和运行机制。
摘要由CSDN通过智能技术生成

一、assert的作用

assert往往用于程序的测试版本阶段,而通常不用于release版本阶段。其于程序员而言,是起到尽早发现程序错误的作用。

assert() 的用法像是一种"契约式编程",其表达的意思就是,若断言在我的假设条件下为真,则能够正常良好的运作,否则调用中断程序终止运行,其实就相当于一个 if 语句:

if(假设成立)
{
     程序正常运行;
}
else
{
      报错&&终止程序!(避免由程序运行引起更大的错误)  
}

assert本身并非一个函数,而是一个宏,其原型定义在"assert.h"库中【第三部分会详细描述】。

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

使用 assert 的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。

题外话,对于Javascript中,没有定义相应的断言,但是可以通过console.assert('假设条件','错误信息')的方式来起到类似的测试作用;或者根据断言的定义,自己设计一个断言函数,若错误直接throw Error

二、assert的用法以及注意事项

1、重要宏定义:NDEBUG和DEBUG
  • NDEBUG:控制assert是否生效, 在调试结束后#define NDEBUG 来禁用 assert 调用。例如:
#include 
#define NDEBUG 
#include "assert.h"

//相当于NDEBUG对assert起到如下作用
#ifdef NDEBUG
#define assert(x) ((void)0)
#else
...
  • DEBUG:代表在测试阶段,可以这样定义宏以控制assert使用
#define DEBUG   // release 版本注释掉即可,测试版本定义
#ifdef DEBUG
#define ASSERT(f) assert(f)
#else 
#define ASSERT(f) ((void)0)
#endif
2、用法示例以及注意事项
  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++ <0 )
    
    //正确
    assert(i < 100)
    i++;

三、assert原理

assert在assert.h中定义的完整代码:

  • 首先我们看到预处理命令 #ifdef NDEBUG 这个 NDEBUG 宏,会在程序编译为release版本时编译器会自动定义 NDEBUG这个宏, 而debug版本不会定义,所以debug时只会执行 #else 里的语句,所以根据以上代码可知,一旦程序为release版本,则assert宏只会被展开为 ((void)0) 。

基本的宏定义知识:在某个宏前面+’#’,则被替换时会在左右加上"",作为字符串。

  • 假设现在非NDEBUG,此时进入执行判断表达式真假,若为假调用中断程序。对关键的定义部分化简得到第二行表达式。
//化简
(void)((!!(expression)) ||(_wassert(_CRT_WIDE(#len>0), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) );

=>(void)( ( !!(expression) ) || ( _wassert( #expression,__FILE__,__LINE__ ) , 0 ) );

运行原理:

1、在debug下传入宏参数,展开为一个或表达式,而release下传入,则只会产生 ((void)0) 
2、根据或表达式的判断结果,如果第一个判断结果为1,则短路,不会再执行后面的语句了;
3、如果第一个判断结果为0,则继续执行下一段语句,下一段语句调用一个函数来中断程序的执行,并打印出相应的信息。
 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

音仔小瓜皮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值