论坛首页 编程语言技术论坛

为c语言实现异常处理机制(全)

浏览 10569 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-06-27  
C

引子:
不管是在c++还是在java中,异常都被认为是一种很优雅的处理错误的机制,而如果想在c语言中使用异常就比较麻烦。但是我们仍然可以使用c语言中强大的setjmp和longjmp函数实现类似于c++的异常处理机制。


有关c语言中setjmp和longjmp的资料可以参考:
C语言中一种更优雅的异常处理机制:http://blog.csdn.net/hello_wyq/archive/2006/06/23/826312.aspx
全面了解setjmp与longjmp的使用:http://blog.csdn.net/hello_wyq/archive/2006/06/16/804040.aspx


基本原理
结合setjmp,将当前的环境变量打包为frame(定义的一个结构名)压到一个异常堆栈(自定义的结构体)中,如果程序段正常运行,将此frame弹出,而如果程序出错,将异常栈的顶部元素弹出,根据这个栈顶元素的frame中保存的环境变量,通过setjmp将环境恢复,然后执行某个错误处理函数,而如果没有相应错误处理函数,重新弹出新的栈顶元素,以跳到更外层的setjmp块进行处理。


主要代码分析
此异常机制的实现大量应用了宏,以实现c++和java中异常处理的语法效果。如何使用见下面的如何使用部分。
try部分,作用见注释:c 代码

  1. //try的作用就是将一个包含环境变量env的except_frame压入Except_stack栈中。   
  2. //其中except_flag为setjmp的返回值,表示异常的状态    
  3. #define try do{ \   
  4.             volatile int except_flag; \   
  5.             Except_frame except_frame; \   
  6.             except_frame.prev = Except_stack; \   
  7.             Except_stack = &except_frame; \   
  8.             except_flag = setjmp(except_frame.env); \   
  9.             if (except_flag == EXCEPT_ENTERED) \   
  10.             {  

最重要的部分要数except_raise函数,检查异常是否被处理,如果未被处理,重新从异常栈中弹出新的frame,以跳到更外层的异常处理块。
catch(e)也是宏,检查当前的frame是否和这个catch块中的e对应,如果对应的话,执行下面的部分进行处理。


因为所有except_frame全部放在栈上,因此可以说这个except_stack利用了程序自动产生的stack机制,只要正确地改变Except_stack的值就可以了,不必再考虑分配的except_frame的释放问题,空间的分配释放全由程序自动生成的stack管理。

如何使用
使用这个异常机制的代码,如下

c 代码

  1. try{      
  2.   S;      
  3. }catch(e1){      
  4.   S1;      
  5. }catch(e2){      
  6.   S2;      
  7. }else_catch{      
  8.   S3;      
  9. }end_try;   

 此相当于c++中的:

  1. try{      
  2.     S;      
  3. }catch(e1){      
  4.     S1;      
  5. }catch(e2){      
  6.     S2;      
  7. }catch(…){      
  8.    S3;      
  9. }    

当前实现的异常机制也支持finally语句,因此下面的代码:

c代码
  1. try{      
  2.     S;      
  3. }catch(e1){      
  4.     S1;      
  5. }finally{      
  6.     S2;      
  7. }end_try;  

相当于java中的:

java 代码
  1. try{      
  2.     S;      
  3. }catch(e1except e1){      
  4.     S1;      
  5. }finally     
  6.     S2;   

源代码

文件:exception.h

c 代码

  1. #ifndef __EXCEPTION_H__   
  2. #define __EXCEPTION_H__   
  3.   
  4. #include <stdio.h></stdio.h>   
  5. #include <setjmp.h></setjmp.h>   
  6. #include <assert.h></assert.h>   
  7.   
  8.   
  9. #define T Except_t   
  10. typedef struct Except_t{   
  11.         char *reason;   
  12. }Except_t;   
  13.            
  14. typedef struct Except_frame{   
  15.         struct Except_frame *prev;   
  16.         jmp_buf env;   
  17.         const char *file;   
  18.         int line;   
  19.         const T* exception;   
  20. }Except_frame;   
  21.   
  22. extern Except_frame *Except_stack;      //全局变量    
  23.   
  24. //异常的状态常量    
  25. enum {EXCEPT_ENTERED=0,EXCEPT_RAISED,   
  26.      EXCEPT_HANDLED,EXCEPT_FINALIZED};    
  27.   
  28. #define throw(e) except_raise(&(e),__FILE__,__LINE__)   
  29.   
  30. #define rethrow except_raise(except_frame.exception,\   
  31.                    except_frame.file,except_frame.line)   
  32.                       
  33. void abort_without_exception(const Except_t *e,const char *file,int line);   
  34.   
  35. //将栈顶元素从栈中弹出,重新抛出    
  36. void except_raise(const T *e,const char *file,int line);   
  37.               
  38. //try的作用就是将一个包含环境变量env的except_frame压入Except_stack栈中。   
  39. //其中except_flag为setjmp的返回值,表示异常的状态    
  40. #define try do{ \   
  41.             volatile int except_flag; \   
  42.             Except_frame except_frame; \   
  43.             except_frame.prev = Except_stack; \   
  44.             Except_stack = &except_frame; \   
  45.             except_flag = setjmp(except_frame.env); \   
  46.             if (except_flag == EXCEPT_ENTERED) \   
  47.             {   
  48.   
  49. //如果和刚刚压入Except_stack中的except_frame对应的longjmp不发生,   
  50. //就将其从栈里面弹出来,而如果发生的话,就恢复这个环境变量所   
  51. //保存的环境,回到setjmp()的位置重新进行处理,这时候except_flag   
  52. //的值为EXCEPT_RAISED    
  53. #define catch(e) \   
  54.                 if(except_flag == EXCEPT_ENTERED) \   
  55.                     Except_stack = Except_stack->prev; \   
  56.             }else if(except_frame.exception == &(e)){ \   
  57.                 except_flag = EXCEPT_HANDLED;   
  58.   
  59. #define try_return \   
  60.                    switch(Except_stack = Except_stack->prev,0) \   
  61.                        defaultreturn    
  62.   
  63. #define catch_else \   
  64.                    if(except_flag == EXCEPT_ENTERED) \   
  65.                        Except_stack = Except_stack->prev; \   
  66.                    }else{ \   
  67.                      except_flag = EXCEPT_HANDLED;   
  68.   
  69. //如果没有相关的处理函数,重新抛出    
  70. #define end_try \   
  71.                 if(except_flag == EXCEPT_ENTERED) \   
  72.                     Except_stack = Except_stack->prev; \   
  73.                 } \   
  74.                 if (except_flag == EXCEPT_RAISED) \   
  75.                     except_raise(except_frame.exception, \   
  76.                         except_frame.file,except_frame.line); \   
  77.                 }while(0)   
  78.                    
  79.                    
  80. #define finally \   
  81.                 if(except_flag == EXCEPT_ENTERED) \   
  82.                     Except_stack = Except_stack->prev; \   
  83.                 }{ \   
  84.                     if(except_flag == EXCEPT_ENTERED) \   
  85.                         except_flag = EXCEPT_FINALIZED;    
  86.   
  87. #undef T   
  88. #endif  

 文件:exception.c

c 代码
  1. #include "exception.h"   
  2.   
  3. Except_frame *Except_stack = NULL;   
  4.   
  5. void except_raise(const Except_t *e,const char *file,int line)   
  6. {   
  7.      Except_frame *p = Except_stack;   
  8.         
  9.      assert(e);   
  10.      if(p == NULL){   
  11.           abort_without_exception(e,file,line);   
  12.      }   
  13.      p->exception = e;   
  14.      p->file = file;   
  15.      p->line = line;   
  16.      Except_stack = Except_stack->prev;   
  17.      longjmp(p->env,EXCEPT_RAISED);   
  18.  }   
  19.   
  20. void abort_without_exception(const Except_t *e,const char *file,int line)   
  21. {   
  22.      fprintf(stderr,"Uncaught exception");   
  23.      if(e->reason)   
  24.          fprintf(stderr," %s",e->reason);   
  25.      else  
  26.          fprintf(stderr," at 0x%p",e);   
  27.         
  28.      if (file && line > 0)   
  29.          fprintf(stderr, "raised at %s:%d\n",file,line);   
  30.      fprintf(stderr,"aborting...\n");   
  31.      fflush(stderr);   
  32.      abort();   
  33. }  
 

参考资料:<o:p></o:p>

c语言接口与实现》 David R Hanson<o:p></o:p>

 

   发表时间:2007-06-27  
if the operating system provides a exception stack which is exposed to user space thru syscalls, it will be quite elegant and easy to implement try/catch in user space in a call-back way.
0 请登录后投票
   发表时间:2007-06-27  
如果s语句中存在一个i++操作,很可能在异常抛出后,i仍然为原值,除非i声明的时候是volatile。

这种异常处理机制很脆弱,但在大多数情况下已经够用。
0 请登录后投票
   发表时间:2007-06-29  
为什么要在C语言里业实现异常呢? 直接使用C++ 不行么?
0 请登录后投票
   发表时间:2007-06-29  
比如嵌入式开发中由于开发环境不支持c++,只能使用c
开发底层东西,很多人都用c,环境所致你也得用c
---没有语言之争的意图
0 请登录后投票
   发表时间:2007-07-01  
可以看看 C interface and implimentation
里面的第四章 专门说这个
0 请登录后投票
   发表时间:2007-07-19  
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
0 请登录后投票
   发表时间:2007-07-19  
javaeye000 写道
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?
0 请登录后投票
   发表时间:2007-07-24  
xombat 写道
javaeye000 写道
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?


不好意思,刚发现你的回复。我是指这个结构,里面的reason需要在抛出异常的地方给它分配空间,处理玩异常
之后再把它释放掉,这样也太麻烦了一点吧。

typedef struct Except_t{  
         char *reason;  
}Except_t;
0 请登录后投票
   发表时间:2007-07-24  
xombat 写道
javaeye000 写道
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?


不好意思,刚发现你的回复。我是指这个结构,里面的reason需要在抛出异常的地方给它分配空间,处理玩异常
之后再把它释放掉,这样也太麻烦了一点吧。

typedef struct Except_t{  
         char *reason;  
}Except_t;
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics