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 
 3 #include <stdio.h>
 4 #include <assert.h>
 5 double diva(double num1,double num2)         //两数相除函数 
 6 {
 7     double re;
 8     re=num1/num2;
 9     return re;
10 }
11 int main()
12 {
13   printf("请输入第一个数字:");
14   scanf("%lf",&a);
15   printf("请输入第二个数字:");
16   scanf("%lf",&b);
17   assert(0!=b);                                //构造断言表达式,捕获预期异常错误
18    result=diva(a,b);
19    printf("相除的结果是: %.2lf\n",result);    
20    return 0;
21 }

3.使用errno全局变量,进行异常处理:

errno全局变量主要在调式中,当系统API函数发生异常的时候,将errno变量赋予一个整数值,根据查看这个值来推测出错的原因。

其中的各个整数值都有一个相应的宏定义,表示不同的异常原因:

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

这里我们就不以前面的除数为0的例子来进行异常处理了,因为我不知道如何定义自己特定错误的errno,如果哪位知道,希望能给出方法。我以一个网上的例子来说明它的使用方法:

 1 代码
 2 
 3 #include <errno.h>  
 4 #include <math.h>  
 5 #include <stdio.h>  
 6 int main(void)  
 7 {  
 8 errno = 0;  
 9 if (NULL == fopen("d:\\1.txt", "rb"))  
10 {  
11 printf("%d", errno);  
12 }  
13 else  
14 {  
15  printf("%d", errno);  
16 }  
17 return 0;  }

这里试图打开一个d盘的文件,如果文件不存在,这是查看errno的值,结果是2、

当文件存在时,errno的值为初始值0。然后查看值为2的错误信息,在宏定义那边#define    ENOFILE        2    /* No such file or directory */
便知道错误的原因了。

4.使用goto语句进行异常处理:

goto语句相信大家都很熟悉,是一个跳转语句,我们还是以除数为0的例子,来构造一个异常处理的例子:

 1 代码
 2 
 3 #include <stdio.h>
 4 double diva(double num1,double num2)         //两数相除函数 
 5 {
 6     double re;
 7     re=num1/num2;
 8     return re;
 9 }
10 int main()
11 {
12   int tag=0;
13   double a,b,result;
14   if(1==tag)
15   {
16       Throw:
17     printf("除数为0,出现异常\n");
18   }
19    tag=1;
20   printf("请输入第一个数字:");
21   scanf("%lf",&a);
22   printf("请输入第二个数字:");
23   scanf("%lf",&b);
24 if(b==0)                                   //捕获异常(或许这么说并不恰当,暂且这么理解)
25   goto Throw;                                //抛出异常 
26   result=diva(a,b);
27    printf("%d\n",errno);
28    printf("相除的结果是: %.2lf\n",result);    
29 
30 return 0;
31 }

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

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

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

代码如下所示:

 1 #include <stdio.h>
 2 #include <setjmp.h>
 3 jmp_buf j;
 4 void Exception(void)
 5 {
 6    longjmp(j,1);
 7 }
 8  double diva(double num1,double num2)         //两数相除函数
 9  {
10     double re;
11      re=num1/num2;
12     return re;
13 }
14  int main()
15 {
16     double a,b,result;
17 
18     
19    printf("请输入第一个数字:");
20    scanf("%lf",&a);
21    printf("请输入第二个数字:");
22   if(setjmp(j)==0)
23   {
24    scanf("%lf",&b);
25    if(0==b)
26    Exception();
27  result=diva(a,b);
28     printf("相除的结果是: %.2lf\n",result);
29   }
30   else
31   printf("试图除以一个为0的数字\n");
32  return 0;
33 }

四 总结:

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值