一 前言:

异常处理,对于做面向对象开发的开发者来说是再熟悉不过了,例如在C#中有

try

{

...

}

catch( Exception e){...}

finally{

.....

}

在C++中,我们常常会使用

try{}

...

catch(){}

块来进行异常处理。

说了那么多,那么到底什么是异常处理呢?

异常处理(又称为错误处理)功能提供了处理程序运行时出现的任何意外或异常情况的方法。

异常处理一般有两种模型,一种是"终止模型",一种是"恢复模型"

"终止模型":在这种模型中,将假设错误非常关键,将以致于程序无法返回到异常发生的地方继续执行.一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行.

"恢复模型":异常处理程序的工作是修正错误,然后重新尝试调动出问题的方法,并认为的二次能成功. 对于恢复模型,通常希望异常被处理之后能继续执行程序.在这种情况下,抛出异常更像是对方法的调用--可以在Java里用这种方法进行配置,以得到类似恢复的行为.(也就是说,不是抛出异常,而是调用方法修正错误.)或者,把try块放在while循环里,这样就可以不断的进入try块,直到得到满意的结果.

二 面向对象中的异常处理

大致了解了什么是异常处理后,由于异常处理在面向对象语言中使用的比较普遍,我们就先以C++为例,做一个关于异常处理的简单例子:

问题:求两个数相除的结果。

这里,隐藏这一个错误,那就是当除数为0时,会出现,所以,我们得使用异常处理来捕捉这个异常,并抛出异常信息。

具体看代码:

1 #include

2 #include

3 using namespace std;

4 class DivideError:public exception

5 {

6  public:

7           DivideError::DivideError():exception(){}

8          const char* what(){

9             return "试图去除一个值为0的数字";

10         }

11

12 };

13 double quotion(int numerator,int denominator)

14 {

15     if(0==denominator)          //当除数为0时,抛出异常16     throw DivideError();

17     return static_cast(numerator)/denominator;

18 }

19 int main()

20 {

21     int number1;             //第一个数字22     int number2;             //第二个数字23     double result;

24     cout<

25     while(cin>>number1>>number2){

26         try{

27             result=quotion(number1,number2);

28             cout<

29

30         }     //end try31         catch(DivideError &divException){

32             cout<

33                 <

34         }

35     }

36

37 }

38

在这个例子中,我们使用了头文件中的exception类,并使DivideError类继承了它,同时重载了虚方法what(),以给出特定的异常信息。

而C#中的异常处理类则封装的更有全面,里面封装了常用的异常处理信息,这里就不多说了。

三 C语言中的异常处理

在C语言中异常处理一般有这么几种方式:

1.使用标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于头文件中。

2.使用assert(断言)宏调用,位于头文件中,当程序出错时,就会引发一个abort()。

3.使用errno全局变量,由C运行时库函数提供,位于头文件中。

4.使用goto语句,当出错时跳转。

5.使用setjmp,longjmp进行异常处理。

接下来,我们就依次对这几种方式来看看到底是怎么做的:

我们仍旧以前面处理除数为0的异常为例子。

1.使用exit()函数进行异常终止:

1 #include

2 #include

3 double diva(double num1,double num2)         //两数相除函数4 {

5     double re;

6     re=num1/num2;

7     return re;

8 }

9 int main()

10 {

11    double a,b,result;

12  printf("请输入第一个数字:");

13   scanf("%lf",&a);

14   printf("请输入第二个数字:");

15   scanf("%lf",&b);

16   if(0==b)                                //如果除数为0终止程序17   exit(EXIT_FAILURE);

18 result=diva(a,b);

19    printf("相除的结果是: %.2lf\n",result);

20 return 0;

21 }

其中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);

具体看如下程序:

1 #include

2 #include

3 void Exception(void)                           //注册终止函数,通过挂接到此函数,捕获异常信息4 {

5     printf("试图去除以一个为0的数字,出现异常!\n");

6 }

7 int main()

8 {

9    double a,b,result;

10   printf("请输入第一个数字:");

11   scanf("%lf",&a);

12   printf("请输入第二个数字:");

13   scanf("%lf",&b);

14   if(0==b)                    //如果除数为0终止程序 ,并挂接到模拟异常捕获的注册函数15   {

16

17   atexit(Exception);

18   exit(EXIT_FAILURE);

19   }

20    result=diva(a,b);

21    printf("相除的结果是: %.2lf\n",result);

22 return 0;

23 }

这里需要注意的是,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的例子,来构造一个异常处理的例子:

代码

5.使用setjmp和longjmp进行异常捕获与处理:

setjmp和longjmp是非局部跳转,类似goto跳转作用,但是goto语句具有局限性,只能在局部进行跳转,当需要跳转到非一个函数内的地方时就需要用到setjmp和longjmp。setjmp函数用于保存程序的运行时的堆栈环境,接下来的其它地方,你可以通过调用longjmp函数来恢复先前被保存的程序堆栈环境。异常处理基本方法:

使用setjmp设置一个跳转点,然后在程序其他地方调用longjmp跳转到该点(抛出异常).

代码如下所示:

#include

#include

jmp_buf j;

void Exception(void)

{

longjmp(j,1);

}

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)==0)

{

scanf("%lf",&b);

if(0==b)

Exception();

result=diva(a,b);

printf("相除的结果是: %.2lf\n",result);

}

else

printf("试图除以一个为0的数字\n");

return 0;

}

四 总结:

除了以上几种方法之外,另外还有使用信号量等等方法进行异常处理。当然在实际开发中每个人都有各种调式的技巧,而且这文章并不是说明异常处理一定要这样做,这只是对一般做法的一些总结,也不要乱使用异常处理,如果弄的不好就严重影响了程序的效率和结构,就像设计模式一样,不能胡乱使用。

c语言常用的异常处理,C语言中的异常处理相关推荐

  1. PHP如何进行错误与异常处理(PHP7中的异常处理和之前版本异常处理的区别)

    PHP如何进行错误与异常处理(PHP7中的异常处理和之前版本异常处理的区别) 一.总结 一句话总结: throwable接口+Error类 在PHP7更新中有一条:更多的Error变为可捕获的Exce ...

  2. c语言常用词汇下载,C语言常用词汇表

    C语言常用词汇表 大小cost花费pay支付area 面积rate速度,比率,价格count 折扣total 总计size 大小sizeof字节data 数据default默认,缺省constant ...

  3. c语言常用单词表格,C语言常用单词

    C语言常用单词 贡献者:游客10727681 类别:英文 时间:2016-09-19 19:31:53 收藏数:131 评分:2.5 返回上页 举报此文章 请选择举报理由: 广告/谣言/欺诈 政治敏感 ...

  4. c#异常处理_C#中的异常处理

    c#异常处理 What an exception is? 有什么例外? An exception is a runtime error; that means an abnormal situatio ...

  5. java中的异常处理语句_Java中实现异常处理的基础知识

    Java中实现异常处理的基础知识 异常 (Exception):发生于程序执行期间,表明出现了一个非法的运行状况.许多JDK中的方法在检测到非法情况时,都会抛出一个异常对象. 例如:数组越界和被0除. ...

  6. c语言常用转向的语句,java中类似于C语言的goto无条件转向语句的语法是什么?...

    满意答案 Java不存在goto语句,但是已经用"类名.方法"的形式进行方法调用. 解释:goto语句作为java的保留字段,但是没有实际的应用的.goto语句实际上是C语言的范畴 ...

  7. c语言常用的库,C语言的一些常用标准库分享

    有很多工程师喜欢自己封装一些标准库已有的函数,其实自己封装的函数,并不一定比标准库好,有时候反而代码更冗余,且有bug.下面就来分享一下C语言常见的一些标准库. 标准头文件包括: 一.标准定义() 文 ...

  8. 单片机c语言常用指令,单片机C语言(C51)的常用库函数.doc

    C51的常用库函数详解 C51语言的编译器中包含有丰富的库函数,使用库函数可以大大简化用户程序设计的工作量,提高编程效率.每个库函数都在相应的头文件中给出了函数原型声明,在使用时,必须在源程序的开始处 ...

  9. 大学c语言常用语句意思,C语言常用语句

    . printf函数调用的一般 printf函数是一个标准库函数,它的函数原型在头文件"stdio.h"中.但作为一个特例,不要求在使用 printf 函数之前必须包含stdio. ...

  10. c语言常用的条件编译,C语言条件编译

    使用与平台有关的C语言函数,可能会使得程序不具有可移植性.比如Socket编程.多线程编程等是与平台有关的. 若想将程序做成平台无关的就需要用到与平台相关的条件编译. 编译器 GCC #ifdef _ ...

最新文章

  1. wincc里c语言long int,WinCC V7.3_C脚本手册.pdf
  2. TODO;FIXME;XXX
  3. 点击别的地方隐藏下拉列表
  4. 源码时代php中级项目,PHP学科项目评比圆满结束
  5. 2.数据中心网络演进
  6. [Erlang 0097] TCP半开的几个小测试
  7. 利用计算机程序解决问题的基本过程,第四章第一节编制计算机程序解决问题
  8. 微服务架构设计模式~根据子域进行服务拆分
  9. Spark-shell 脚本批量执行命令,命令行批量执行命令
  10. C语言i++和++i的区别和用法
  11. Navicat Premium 简体中文版 12.0.16 以上版本国外官网下载地址(非国内)
  12. java求职英文简历范本2篇_Java英文简历范文
  13. 机顶盒系统升级服务器,服务器及其软件升级方法、IP机顶盒及其软件升级方法...
  14. python aiml开发文档,带有Python的AIML文件
  15. 小程序中如何引用阿里图标
  16. ui效果图生成html,四步制作一个高大上的iPhone效果图模版 UI呀-学UI设计网
  17. linux服务器拷机测试,服务器拷机使用方法
  18. PHPstudy 数据库基本操作
  19. 书论95 蒋骥《续书法论》
  20. 为什么是三次握手和四次挥手

热门文章

  1. poj 3461 Oulipo(kmp统计子串出现次数)
  2. UA MATH564 概率论 QE练习题 概率极限理论
  3. C# mschart 控件的基本使用和图表控件Series的概念
  4. Java虚拟机new和newarray相关指令学习
  5. 简单web服务器学习总结
  6. 学习笔记101— word 如何给某一页后面所有页增加行号
  7. elementUI树状图竖向滚动条和横向滚动条问题
  8. 微信小程序工具真机调试提示page xxx/xxx/xxx is not found
  9. java子类对象不能调用父类protected方法和域的原因。
  10. POJ 3616 Milking Time