C语言try catch 异常检测实现

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

文件:exception.c

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

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

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

分享到:
评论
9 楼 javaeye000 2007-07-24
xombat 写道
javaeye000 写道
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?


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

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


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

typedef struct Except_t{
char *reason;
}Except_t;
7 楼 xombat 2007-07-19
javaeye000 写道
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?
6 楼 javaeye000 2007-07-19
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
5 楼 mathgl 2007-07-01
可以看看 C interface and implimentation
里面的第四章 专门说这个
4 楼 xombat 2007-06-29
比如嵌入式开发中由于开发环境不支持c++,只能使用c
开发底层东西,很多人都用c,环境所致你也得用c
---没有语言之争的意图
3 楼 zuowj 2007-06-29
为什么要在C语言里业实现异常呢? 直接使用C++ 不行么?
2 楼 xombat 2007-06-27
如果s语句中存在一个i++操作,很可能在异常抛出后,i仍然为原值,除非i声明的时候是volatile。

这种异常处理机制很脆弱,但在大多数情况下已经够用。
1 楼 jigsaw 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
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值