C语言断言函数assert()的学习笔记

  在C语言库函数中提供了一个辅助调试程序的小型库,它是由assert()宏组成,接收一个整形表达式作为参数。如果表达式的值为假(非零),则assert()宏就在标准错误流(stderr)中写入一条错误信息,并调用abort()函数终止程序。

  下面通过一个简单的例子来看一下assert()的用法。

int main()
{
	int n = 1;
	assert(n>=0);
	printf("%d \r\n",n);
	system("pause");
	return 0;
}

  在assert()中表达式判断的条件是 n > 0 ,那么当整形变量n的值小于0时,就说明表达式为假,断言函数就会起作用。这里先试一下正常情况,将n的值设置为1,输出结果如下:

  接下来将 n 的值改为 -1,继续测试。

  此时程序异常终止了。下面详细分析一下,这个函数的执行过程。

  在assert.h头文件中可以查看到assert()的原型,这里有两个原型,一个是当定义了 _UNICODE 或者 UNICODE 时调用的是第一个assert(),当没有定义时,调用的是第二个assert()。由于在头文件中没有定义 _UNICODE 或 UNICODE,所以这里调用的是第二个assert。下面开始分析这条宏定义语句。

#define assert(_Expression) (void) 	((!!(_Expression)) ||  (_assert(#_Expression,__FILE__,__LINE__),0))

void __cdecl _assert (const char *_Message, const char *_File, unsigned _Line);

  首先 assert 中传入了一个参数 _Expression ,接着还有有一条语句,里面有两部分由或运算符连接。对于或运算符 || 来说,当第一个条件成立时就不会执行第二个条件,只有当第一个条件不成立时才会执行第二条语句。

((!!(_Expression)) ||  (_assert(#_Expression,__FILE__,__LINE__),0))

  首先来看第一个判断条件 (!!(_Expression)),给传入的参数取了两次非,也就相当于变量本身,当 _Expression 为真时,就不会执行 第二个条件,只有 _Expression 为假时,才会执行第二个条件。在程序中当 n > 0 成立时就不会执行第二个条件,当 n > 0 不成立时,才会执行第二条语句,所以在上面测试中,当 n = -1 时,程序才会异常终止。

  接下来看第二个条件 (_assert(#_Expression,FILE,LINE),0) 这是一个函数,它的原型是:

void __cdecl _assert (const char *_Message, const char *_File, unsigned _Line);

  这个函数有三个参数,根据这三个参数的名称大概可以推断出,第一个参数是用来存储信息内容,第二个参数是用来表示当前文件的详细信息,第三个参数表示代码中那一行出错。

  这三个参数和控制台打印的内容相符合,Program 后面跟着的是当前运行的可执行文件路径,File 后面跟着的是报错的文件路径,Line 后面跟着的是 出错的具体位置,说明在 test6.c 文件中第16行程序出现了错误,最后一行提示出错的表达式 为 n>=0 ,说明变量n 大于等于0这个条件不成立,也就是当前变量n的值小于0,所以引发了程序异常。

  通过上面的分析可以看出,assert()对应调试程序来说很有帮助,像这种隐藏在代码中的错误在编译程序的时候,编译器是检测不出来的,只有当程序执行的时候才会发现。所以通过assert()来检测表达式就可以快速的定位程序的bug。

  加入不想使用assert()来检测了,不需要修改代码,只需要在assert.h中定义 NDEBUG就行了。

 在assert.h中添加宏定义,继续运行程序。

  此时n的值为-1,但是程序正常的打印出了-1,并没有报错。说明assert()不检测错误了。这个从头文件中也可以看出。

  当定义了NDEBUG之后,**assert(_Expression)**执行的具体函数就变成了 ((void)0),也就是啥也不干了。所以当在头文件中定义了NDEBUG之后,assert()的检测功能就自动失效了。这样在调试程序的时候,只需要一条语句就可以开启或者关闭调试信息输出了。

到此这篇关于C语言断言函数assert()的学习笔记的文章就介绍到这了,更多相关C语言断言函数assert()内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅析C语言中assert的用法

    assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:#include <assert.h>void assert( int expression );assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行.请看下面的程序清单badptr.c: 复制代码 代码如下: #include <stdio.h>#incl

  • C语言断言函数assert()的学习笔记

      在C语言库函数中提供了一个辅助调试程序的小型库,它是由assert()宏组成,接收一个整形表达式作为参数.如果表达式的值为假(非零),则assert()宏就在标准错误流(stderr)中写入一条错误信息,并调用abort()函数终止程序.   下面通过一个简单的例子来看一下assert()的用法. int main() { int n = 1; assert(n>=0); printf("%d \r\n",n); system("pause"); retu

  • PHP中断言函数的使用详解

    原来一直以为断言相关的函数是 PHPUnit 这些单元测试组件提供的,在阅读手册后才发现,这个 assert() 断言函数是 PHP 本身就自带的一个函数.也就是说,我们在代码中进行简单的测试的时候是不需要完全引入整个单元测试组件的. assert() 断言函数 assert(1==1); assert(1==2); // assert.exception = 0 时,Warning: assert(): assert(1 == 2) // assert.exception = 1 时,Fata

  • R语言学习笔记之lm函数详解

    在使用lm函数做一元线性回归时,发现lm(y~x+1)和lm(y~x)的结果是一致的,一直没找到两者之间的区别,经过大神们的讨论和测试,才发现其中的差别,测试如下: ------------------------------------------------------------- ------------------------------------------------------------- 结果可以发现,两者的结果是一样的,并无区别,但是若改为lm(y~x-1)就能看出+1和

  • Python基础语言学习笔记总结(精华)

    以下是Python基础学习内容的学习笔记的全部内容,非常的详细,如果你对Python语言感兴趣,并且针对性的系统学习一下基础语言知识,下面的内容能够很好的满足你的需求,如果感觉不错,就收藏以后慢慢跟着学习吧. 一.变量赋值及命名规则 ① 声明一个变量及赋值 #!/usr/bin/env python # -*- coding:utf-8 -*- # _author_soloLi name1="solo" name2=name1 print(name1,name2) name1 = &q

  • Go语言学习笔记之反射用法详解

    本文实例讲述了Go学习笔记之反射用法.分享给大家供大家参考,具体如下: 一.类型(Type) 反射(reflect)让我们能在运行期探知对象的类型信息和内存结构,这从一定程度上弥(mi)补了静态语言在动态行为上的不足.同时,反射还是实现元编程的重要手段. 和 C 数据结构一样,Go 对象头部并没有类型指针,通过其自身是无法在运行期获知任何类型相关信息的.反射操作所需要的全部信息都源自接口变量.接口变量除存储自身类型外,还会保存实际对象的类型数据. func TypeOf(i interface{

  • JavaScript学习笔记(三):JavaScript也有入口Main函数

    在C和Java中,都有一个程序的入口函数或方法,即main函数或main方法.而在JavaScript中,程序是从JS源文件的头部开始运行的.但是某种意义上,我们仍然可以虚构出一个main函数来作为程序的起点,这样一来不仅可以跟其他语言统一了,而且说不定你会对JS有更深的理解. 1. 实际的入口 当把一个JavaScript文件交给JS引擎执行时,JS引擎就是从上到下逐条执行每条语句的,直到执行完所有代码. 2. 作用域链.全局作用域和全局对象 我们知道,JS中的每个函数在执行时都会产生一个新的

  • javascript学习笔记(四)function函数部分

    函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块. Jscript 支持两种函数:一类是语言内部的函数(如eval() ),另一类是自己创建的. 在 JavaScript 函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它.(该变量的作用域是局部的). 您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量. 函数的调用方式 1.普通调用:functionName(实际参数...) 2.通过指向函数的变量去调用: var  myVar

  • JavaScript函数、闭包、原型、面向对象学习笔记

    断言 单元测试框架的核心是断言方法,通常叫assert(). 该方法通常接收一个值--需要断言的值,以及一个表示该断言目的的描述. 如果该值执行的结果为true,断言就会通过: 否则,断言就会被认为是失败的. 通常用一个相应的通过(pass)/ 失败(fail)标记记录相关的信息: function assert(value, desc) { let li = document.createElement('li'); li.className = value ? 'pass' : 'fail'

  • python 匿名函数与三元运算学习笔记

    匿名函数 匿名函数就是不需要显示式的指定函数名 首先看一行代码: def calc(x,y):     return x*y print(calc(2,3))   # 换成匿名函数   calc = lambda x,y:x*y print(calc(2,3)) 你也许会说,用上这个东西没感觉有毛方便呀, ....呵呵,如果是这么用,确实没毛线改进,不过匿名函数主要是和其它函数搭配使用的呢,如下 res = map(lambda x:x**2,[1,2,3,4,5]) print(list(re

  • R语言学习笔记缺失数据的Bootstrap与Jackknife方法

    目录 一.题目 二.解答 a)Bootstrap与Jackknife进行估计 b)均值与变异系数(大样本)的标准差解析式推导与计算 c)缺失插补前的Bootstrap与Jackknife d)比较各种方式的90%置信区间情况(重复100次实验) 填补之前进行Bootstrap或Jackknife 填补之后进行Bootstrap或Jackknife 一.题目 下面再加入缺失的情况来继续深入探讨,同样还是如习题1.6的构造方式来加入缺失值,其中a=2, b = 0 我们将进行如下几种操作: 二.解答

随机推荐