C语言中之慌处理

一 前言:

特别处理,对于做面向对象开发之开发者来说是更熟习但是了,例如在C#中有

try

{

     …

}

catch( Exception e){…}

finally{

…..

}

于C++中,我们平日会使

try{}

catch(){}

片来展开充足处理。

说了那么多,那么究竟什么是充分处理吧?

好处理(又称作错误处理)功能提供了处理程序运行时起的此外不测依旧相当情况的法子。

大处理一般暴发有限种模型,一种是”终止模型”,一栽是”苏醒模型”

“终止模型”:在这种模型中,将借而错误异常关键,将导致于次不能回来到深出的地点继续执行.一旦好被废来,就标明错误就不能够挽回,也未可知回到继续执行.

“復苏模型”:非凡处理程序的干活是更正错误,然后再尝试调动起问题之方,并觉得的第二不佳会成功.
对于復苏模型,平日要丰富被处理未来能继续执行程序.于这种情况下,抛来异常更如是针对章程的调用–可以于Java里之所以这种模式举行布置,以获近似复苏的行为.(也就是说,不是废来老,而是调用方法修正错误.)或者,把try块放在while循环里,这样即使好穿梭的进入try块,直到得到满足的结果.


其次 面向对象中的老处理

大体了然了哟是死处理后,由于老处理在面向对象语言中以的于大,我们不怕先行以C++为例,做一个有关丰硕处理的简要例子:

问题:求少单数相除的结果。

这里,隐藏这个谬误,这就是是当除数为0时,会晤世,所以,我们得用大处理来捕捉这么些十分,并丢掉来分外消息。

现实看代码:

 

 1 #include <iostream>
 2 #include <exception>
 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<double>(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<<“结果是 :”<<result<<endl;
29             
30         }     //end try
31         catch(DivideError &divException){
32             cout<<“发生相当:”
33                 <<divException.what()<<endl;
34         }
35     }
36     
37 }
38 

 

 

在这些事例中,大家运用了<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()函数举办特别终止:

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 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 <stdio.h>
 2 #include <stdlib.h>
 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,程序报错误,并已执行。

代码如下:

 

C++ 1C++ 2代码

#include <stdio.h>
#include <assert.h>
double diva(double num1,double num2)         //两数相除函数 
{
    double re;
    re=num1/num2;
    return re;
}
int main()
{
  printf(“请输入第一单数字:”);
  scanf(“%lf”,&a);
  printf(“请输入第二独数字:”);
  scanf(“%lf”,&b);
  assert(0!=b);                                //构造断言表达式,捕获预期好错误
   result=diva(a,b);
   printf(“相除的结果是: %.2lf\n”,result);    
   return 0;
}

3.动errno全局变量,举行非凡处理:

errno全局变量首要在调式中,当系统API函数暴发非常的时光,将errno变量赋予一个整数价,遵照查看此价来想出错的缘故。

个中的一一整数值都发出一个对应的宏定义,表示不同之那么些原因:

 

C++ 3C++ 4代码

#define EPERM        1    /* Operation not permitted */
#define    ENOFILE        2    /* No such file or directory */
#define    ENOENT        2
#define    ESRCH        3    /* No such process */
#define    EINTR        4    /* Interrupted function call */
#define    EIO        5    /* Input/output error */
#define    ENXIO        6    /* No such device or address */
#define    E2BIG        7    /* Arg list too long */
#define    ENOEXEC        8    /* Exec format error */
#define    EBADF        9    /* Bad file descriptor */
#define    ECHILD        10    /* No child processes */
#define    EAGAIN        11    /* Resource temporarily unavailable */
#define    ENOMEM        12    /* Not enough space */
#define    EACCES        13    /* Permission denied */
#define    EFAULT        14    /* Bad address */
/* 15 – Unknown Error */
#define    EBUSY        16    /* strerror reports “Resource device” */
#define    EEXIST        17    /* File exists */
#define    EXDEV        18    /* Improper link (cross-device link?) */
#define    ENODEV        19    /* No such device */
#define    ENOTDIR        20    /* Not a directory */
#define    EISDIR        21    /* Is a directory */
#define    EINVAL        22    /* Invalid argument */
#define    ENFILE        23    /* Too many open files in system */
#define    EMFILE        24    /* Too many open files */
#define    ENOTTY        25    /* Inappropriate I/O control operation */
/* 26 – Unknown Error */
#define    EFBIG        27    /* File too large */
#define    ENOSPC        28    /* No space left on device */
#define    ESPIPE        29    /* Invalid seek (seek on a pipe?) */
#define    EROFS        30    /* Read-only file system */
#define    EMLINK        31    /* Too many links */
#define    EPIPE        32    /* Broken pipe */
#define    EDOM        33    /* Domain error (math functions) */
#define    ERANGE        34    /* Result too large (possibly too small) */
/* 35 – Unknown Error */
#define    EDEADLOCK    36    /* Resource deadlock avoided (non-Cyg) */
#define    EDEADLK        36
C++,/* 37 – Unknown Error */
#define    ENAMETOOLONG    38    /* Filename too long (91 in Cyg?) */
#define    ENOLCK        39    /* No locks available (46 in Cyg?) */
#define    ENOSYS        40    /* Function not implemented (88 in Cyg?) */
#define    ENOTEMPTY    41    /* Directory not empty (90 in Cyg?) */
#define    EILSEQ        42    /* Illegal byte sequence */

这里我们就是未因前边的除数为0的例证来进展丰富处理了,因为自庚午亮堂哪定义自己一定错误的errno,即便哪位领会,希望会叫出艺术。我因为一个网上的事例来阐明它的采取办法:

 

C++ 5C++ 6代码

#include <errno.h>  
#include <math.h>  
#include <stdio.h>  
int main(void)  
{  
errno = 0;  
if (NULL == fopen(“d:\\1.txt”, “rb”))  
{  
printf(“%d”, errno);  
}  
else  
{  
 printf(“%d”, errno);  
}  
return 0;  }   

此间准备打开一个d盘的文件,假若文件未存在,这是查看errno的值,结果是2、

当文件在时时,errno的值吗开首值0。然后查看值为2的错误音信,在宏定义那边#define    ENOFILE        2    /* No such file or directory */
不畏亮不当的原因了。

4.采取goto语句子举办特别处理:

goto语句相信我们还怪熟识,是一个跨越反语句,我们或以除数为0的事例,来协会一个良处理的例子:

 

C++ 7C++ 8代码

#include <stdio.h>
double diva(double num1,double num2)         //两数相除函数 
{
    double re;
    re=num1/num2;
    return re;
}
int main()
{
  int tag=0;
  double a,b,result;
  if(1==tag)
  {
      Throw:
    printf(“除数为0,出现非凡\n”);
  }
   tag=1;
  printf(“请输入第一只数字:”);
  scanf(“%lf”,&a);
  printf(“请输入第二个数字:”);
  scanf(“%lf”,&b);
if(b==0)                                   //捕获特别(或许这样说并无合适,暂且这么清楚)
  goto Throw;                                //抛出好 
  result=diva(a,b);
   printf(“%d\n”,errno);
   printf(“相除的结果是: %.2lf\n”,result);    

return 0;
}

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,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;
}

 

 

四 总结:

 

除去以上几乎栽艺术外,其它还有使用信号量等等模式开展好处理。当然在骨子里支出被每个人都发各样调式的技巧,而且就著作并无是表达很处理得要这么做,这只是对一般做法的一部分总,也毫不乱使用卓殊处理,倘诺搞的不得了就是重影响了先后的功效和结构,就比如设计情势一样,不克混使用。