C语言中的异常处理
一 前言:
异常处理,对于做面向对象开发的开发者来说是再熟悉不过了,例如在C#中有
try
{
...
}
catch( Exception e){...}
finally{
.....
}
在C++中,我们常常会使用
try{}
...
catch(){}
块来进行异常处理。
说了那么多,那么到底什么是异常处理呢?
异常处理(又称为错误处理)功能提供了处理程序运行时出现的任何意外或异常情况的方法。
异常处理一般有两种模型,一种是"终止模型",一种是"恢复模型"
"终止模型":在这种模型中,将假设错误非常关键,将以致于程序无法返回到异常发生的地方继续执行.一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行.
"恢复模型":异常处理程序的工作是修正错误,然后重新尝试调动出问题的方法,并认为的二次能成功. 对于恢复模型,通常希望异常被处理之后能继续执行程序.在这种情况下,抛出异常更像是对方法的调用--可以在Java里用这种方法进行配置,以得到类似恢复的行为.(也就是说,不是抛出异常,而是调用方法修正错误.)或者,把try块放在while循环里,这样就可以不断的进入try块,直到得到满意的结果.
二 面向对象中的异常处理
大致了解了什么是异常处理后,由于异常处理在面向对象语言中使用的比较普遍,我们就先以C++为例,做一个关于异常处理的简单例子:
问题:求两个数相除的结果。
这里,隐藏这一个错误,那就是当除数为0时,会出现,所以,我们得使用异常处理来捕捉这个异常,并抛出异常信息。
具体看代码:
#include <iostream> #include <exception> usingnamespace std; class DivideError:public exception { public: DivideError::DivideError():exception(){} constchar* what(){ return"试图去除一个值为0的数字"; } }; double quotion(int numerator,int denominator) { if(==denominator) //当除数为0时,抛出异常 throw DivideError(); return static_cast<double>(numerator)/denominator; } int main() { int number1; //第一个数字 int number2; //第二个数字 double result; cout<<"请输入两个数字:" ; while(cin>>number1>>number2){ try{ result=quotion(number1,number2); cout<<"结果是 :"<<result<<endl; } //end try catch(DivideError &divException){ cout<<"产生异常:" <<divException.what()<<endl; } } }
在这个例子中,我们使用了<expection>头文件中的exception类,并使DivideError类继承了它,同时重载了虚方法what(),以给出特定的异常信息。
而C#中的异常处理类则封装的更有全面,里面封装了常用的异常处理信息,这里就不多说了。
三 C语言中的异常处理
在C语言中异常处理一般有这么几种方式:
1.使用标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于<stdlib.h>头文件中。
2.使用assert(断言)宏调用,位于头文件<assert.h>中,当程序出错时,就会引发一个abort()。
3.使用errno全局变量,由C运行时库函数提供,位于头文件<errno.h>中。
4.使用goto语句,当出错时跳转。
5.使用setjmp,longjmp进行异常处理。
接下来,我们就依次对这几种方式来看看到底是怎么做的:
我们仍旧以前面处理除数为0的异常为例子。
1.使用exit()函数进行异常终止:
#include <stdio.h> #include <stdlib.h> double diva(double num1,double num2) //两数相除函数 { double re; re=num1/num2; return re; } int main() { double a,b,result; printf("请输入第一个数字:"); scanf("%lf",&a); printf("请输入第二个数字:"); scanf("%lf",&b); if(==b) //如果除数为0终止程序 exit(EXIT_FAILURE); result=diva(a,b); printf("相除的结果是: %.2lf\n",result); return; }
其中exit的定义如下:
_CRTIMP void __cdecl __MINGW_NOTHROW exit (int) __MINGW_ATTRIB_NORETURN;
exit的函数原型:void exit(int)由此,我们也可以知道EXIT_FAILURE宏应该是一个整数,exit()函数的传递参数是两个宏,一个是刚才看到的EXIT_FAILURE,还有一个是EXIT_SUCCESS从字面就可以看出一个是出错后强制终止程序,而一个是程序正常结束。他们的定义是:
#define EXIT_SUCCESS 0 #define EXIT_FAILURE 1
到此,当出现异常的时候,程序是终止了,但是我们并没有捕获到异常信息,要捕获异常信息,我们可以使用注册终止函数atexit(),它的原型是这样的:int atexit(atexit_t func);
具体看如下程序:
#include <stdio.h> #include <stdlib.h> void Exception(void) //注册终止函数,通过挂接到此函数,捕获异常信息 { printf("试图去除以一个为0的数字,出现异常!\n"); } int main() { double a,b,result; printf("请输入第一个数字:"); scanf("%lf",&a); printf("请输入第二个数字:"); scanf("%lf",&b); if(==b) //如果除数为0终止程序 ,并挂接到模拟异常捕获的注册函数 { atexit(Exception); exit(EXIT_FAILURE); } result=diva(a,b); printf("相除的结果是: %.2lf\n",result); return; }
这里需要注意的是,atexit()函数总是被执行的,就算没有exit()函数,当程序结束时也会被执行。并且,可以挂接多个注册函数,按照堆栈结构进行执行。abort()函数与exit()函数类似,当出错时,能使得程序正常退出,这里就不多说了。
2.使用assert()进行异常处理:
assert()是一个调试程序时经常使用的宏,切记,它不是一个函数,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。 另外需要注意的是:assert只有在Debug版本中才有效,如果编译为Release版本则被忽略。
我们就前面的问题,使用assert断言进行异常终止操作:构造可能出现出错的断言表达式:assert(number!=0)这样,当除数为0的时候,表达式就为false,程序报告错误,并终止执行。
代码如下:
3.使用errno全局变量,进行异常处理:
errno全局变量主要在调式中,当系统API函数发生异常的时候,将errno变量赋予一个整数值,根据查看这个值来推测出错的原因。
其中的各个整数值都有一个相应的宏定义,表示不同的异常原因:
这里我们就不以前面的除数为0的例子来进行异常处理了,因为我不知道如何定义自己特定错误的errno,如果哪位知道,希望能给出方法。我以一个网上的例子来说明它的使用方法:
这里试图打开一个d盘的文件,如果文件不存在,这是查看errno的值,结果是2、
当文件存在时,errno的值为初始值0。然后查看值为2的错误信息,在宏定义那边#define ENOFILE 2 /* No such file or directory */便知道错误的原因了。
4.使用goto语句进行异常处理:
goto语句相信大家都很熟悉,是一个跳转语句,我们还是以除数为0的例子,来构造一个异常处理的例子:
return; }
5.使用setjmp和longjmp进行异常捕获与处理:
setjmp和longjmp是非局部跳转,类似goto跳转作用,但是goto语句具有局限性,只能在局部进行跳转,当需要跳转到非一个函数内的地方时就需要用到setjmp和longjmp。setjmp函数用于保存程序的运行时的堆栈环境,接下来的其它地方,你可以通过调用longjmp函数来恢复先前被保存的程序堆栈环境。异常处理基本方法:
使用setjmp设置一个跳转点,然后在程序其他地方调用longjmp跳转到该点(抛出异常).
代码如下所示:
#include <stdio.h> #include <setjmp.h> jmp_buf j; void Exception(void) { longjmp(j,); } double diva(double num1,double num2) //两数相除函数 { double re; re=num1/num2; return re; } int main() { double a,b,result;
printf("请输入第一个数字:"); scanf("%lf",&a); printf("请输入第二个数字:"); if(setjmp(j)==) { scanf("%lf",&b); if(==b) Exception(); result=diva(a,b); printf("相除的结果是: %.2lf\n",result); } else printf("试图除以一个为0的数字\n"); return; }
四 总结:
除了以上几种方法之外,另外还有使用信号量等等方法进行异常处理。当然在实际开发中每个人都有各种调式的技巧,而且这文章并不是说明异常处理一定要这样做,这只是对一般做法的一些总结,也不要乱使用异常处理,如果弄的不好就严重影响了程序的效率和结构,就像设计模式一样,不能胡乱使用。
http://www.cnblogs.com/vimsk/archive/2010/12/11/1901698.html#top
C语言中的异常处理的更多相关文章
- Java语言中的异常处理
Java语言中的异常处理包括声明异常.抛出异常.捕获异常和处理异常四个环节. throw用于抛出异常. throws关键字可以在方法上声明该方法要抛出的异常,然后在方法内部通过throw抛出异 ...
- 【C++】异常简述(一):C语言中的异常处理机制
人的一生会遇到很多大起大落,尤其是程序员. 程序员写好的程序,论其消亡形式无非三种:无疾而终.自杀.他杀. 当然作为一名程序员,最乐意看到自己写的程序能够无疾而终,因此尽快的学习异常处理机制是非常重要 ...
- C语言中的异常处理机制
#define try if(!setjmp(Jump_Buffer)) 返回try现场后重新执行判断,所以有两次执行. http://blog.csdn.net/tian_dao_chou_qin/ ...
- 021_go语言中的异常处理
代码演示 package main import ( "errors" "fmt" ) // Go语言里面约定错误代码是函数的最后一个返回值, // 并且类型是 ...
- go语言中使用defer、panic、recover处理异常
go语言中的异常处理,没有try...catch等,而是使用defer.panic.recover来处理异常. 1.首先,panic 是用来表示非常严重的不可恢复的错误的.在Go语言中这是一个内置函数 ...
- C中的异常处理
1,C 语言崇尚简洁高效,因此语言本身并没有异常处理的相关语法规则,但是异常处理在 C 语言中 是存在的,我们有必要从 C 语言开始先看一看 C 语言中的异常处理是怎样, 然后对比 C++ 里面的异常 ...
- Go语言中异常处理painc()和recover()的用法
Go语言中异常处理painc()和recover()的用法 1.Painc用法是:用于抛出错误.Recover()用法是:将Recover()写在defer中,并且在可能发生panic的地方之前,先调 ...
- [R]R语言里的异常处理与错误控制
之前一直只是在写小程序脚本工具,几乎不会对异常和错误进行控制和处理. 随着脚本结构和逻辑更复杂,脚本输出结果的准确性验证困难,同时已发布脚本的维护也变得困难.所以也开始考虑引入异常处理和测试工具的事情 ...
- java 中的异常处理
一. 异常的概念和Java异常体系结构 异常是程序运行过程中出现的错误.本文主要讲授的是Java语言的异常处理.Java语言的异常处理框架, 是Java语言健壮性的一个重要体现. Java把 ...
随机推荐
- 二进制部署kubernetes集群(上篇)
1.实验架构 1.1.硬件环境 准备5台2c/2g/50g虚拟机,使用10.4.7.0/24 网络 .//因后期要直接向k8s交付java服务,因此运算节点需要4c8g.不交付服务,全部2c2g足够. ...
- MyBatis_[tp_50]_动态sql_bind绑定 与原生sql对比
笔记要点出错分析与总结 更推荐,原生的sql写法,bind方法不灵活! Test中: e.setLastName("%e%"); 直接在这里写上模糊查询的语句,更加省时 配置中: ...
- Mybatis-Plus 插件学习
官方指南 1.逻辑删除 在相应字段上添加注解 @TableLogic private Integer deleted; 说明: 使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会) ...
- 使用itchat进行自动微信聊天
import itchat def we_chat(message): #enableCmdQR=2用于linux中显示二维码,hotReload=True退出程序后暂存登录状态 itchat.aut ...
- codeforces#571Div2 D---Vus the Cossack and Numbers【贪心】
题目:http://codeforces.com/contest/1186/problem/D 题意:给定一个大小为$n$的浮点序列,这$n$个数的和为0. 现在对这个序列中的每个数,进行向上取整或向 ...
- 微信小程序学习记录(一)
如何定义一个全局变量: 1,在根目录下app.js中添加 App({ globalData: { g_isPlayingMusic : false, g_currentMusicPostId :nul ...
- [Dart] Manipulate Lists/Arrays in Dart
We will learn how to work with Lists using a variety of methods made available in the dart:core libr ...
- 遍历器Iterator--指针对象
一. 什么是遍历器 1. 遍历器对象(Iterator) 遍历器对象本质上是一个指针对象,该对象有一个next方法,调用next方法返回一个 含有value和done属性的对象{value: val/ ...
- 转,SqlServer 基础之(触发器)
概念: 触发器(trigger)是SQL server 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触 ...
- 学到了林海峰,武沛齐讲的Day30 完 TCP UDP
TCP UDP 其中讲了数据的传输.各有利弊 个人理解 就是这样将高并发,低数据,高数据的传输,稳定高效