断言(Assert)使用、原理以及注意事项详解「建议收藏」

断言(Assert)使用、原理以及注意事项详解「建议收藏」NDEBUG:控制assert是否生效,在调试结束后#defineNDEBUG来禁用assert调用

一、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中定义的完整代码:

断言(Assert)使用、原理以及注意事项详解「建议收藏」

  • 首先我们看到预处理命令 #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,则继续执行下一段语句,下一段语句调用一个函数来中断程序的执行,并打印出相应的信息。
 

今天的文章断言(Assert)使用、原理以及注意事项详解「建议收藏」分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/88941.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注