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,程序报错误,并停止执行。

代码如下:

 

图片 1图片 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变量赋予一个整数值,根据查看此价来揆度出错的因由。

里面的相继整数值都发出一个遥相呼应的宏定义,表示不同的很原因:

 

图片 3图片 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
/* 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,如果哪位了解,希望会于起方。我因为一个网上的例子来验证其的运方式:

 

图片 5图片 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的例子,来布局一个老处理的例证:

 

图片 7图片 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;
}

 

 

四 总结:

 

除上述几乎栽方法外,另外还有使用信号量等等方式开展特别处理。当然在实际上支付被每个人犹有各种调式的技能,而且这篇并无是印证很处理得要如此做,这仅是针对一般做法的有些总,也无须乱使用非常处理,如果来的糟糕就是严重影响了次的效率与组织,就如设计模式一样,不克混使用。