引子:
不管是在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 代码
- //try的作用就是将一个包含环境变量env的except_frame压入Except_stack栈中。
- //其中except_flag为setjmp的返回值,表示异常的状态
- #define try do{ \
- volatile int except_flag; \
- Except_frame except_frame; \
- except_frame.prev = Except_stack; \
- Except_stack = &except_frame; \
- except_flag = setjmp(except_frame.env); \
- if (except_flag == EXCEPT_ENTERED) \
- {
最重要的部分要数except_raise函数,检查异常是否被处理,如果未被处理,重新从异常栈中弹出新的frame,以跳到更外层的异常处理块。
catch(e)也是宏,检查当前的frame是否和这个catch块中的e对应,如果对应的话,执行下面的部分进行处理。
因为所有except_frame全部放在栈上,因此可以说这个except_stack利用了程序自动产生的stack机制,只要正确地改变Except_stack的值就可以了,不必再考虑分配的except_frame的释放问题,空间的分配释放全由程序自动生成的stack管理。
如何使用
使用这个异常机制的代码,如下
c 代码
- try{
- S;
- }catch(e1){
- S1;
- }catch(e2){
- S2;
- }else_catch{
- S3;
- }end_try;
此相当于c++中的:
- try{
- S;
- }catch(e1){
- S1;
- }catch(e2){
- S2;
- }catch(…){
- S3;
- }
当前实现的异常机制也支持finally语句,因此下面的代码:
- try{
- S;
- }catch(e1){
- S1;
- }finally{
- S2;
- }end_try;
相当于java中的:
- try{
- S;
- }catch(e1except e1){
- S1;
- }finally
- S2;
源代码
文件:exception.h
c 代码
- #ifndef __EXCEPTION_H__
- #define __EXCEPTION_H__
- #include
- #include
- #include
- #define T Except_t
- typedef struct Except_t{
- char *reason;
- }Except_t;
- typedef struct Except_frame{
- struct Except_frame *prev;
- jmp_buf env;
- const char *file;
- int line;
- const T* exception;
- }Except_frame;
- extern Except_frame *Except_stack; //全局变量
- //异常的状态常量
- enum {EXCEPT_ENTERED=0,EXCEPT_RAISED,
- EXCEPT_HANDLED,EXCEPT_FINALIZED};
- #define throw(e) except_raise(&(e),__FILE__,__LINE__)
- #define rethrow except_raise(except_frame.exception,\
- except_frame.file,except_frame.line)
- void abort_without_exception(const Except_t *e,const char *file,int line);
- //将栈顶元素从栈中弹出,重新抛出
- void except_raise(const T *e,const char *file,int line);
- //try的作用就是将一个包含环境变量env的except_frame压入Except_stack栈中。
- //其中except_flag为setjmp的返回值,表示异常的状态
- #define try do{ \
- volatile int except_flag; \
- Except_frame except_frame; \
- except_frame.prev = Except_stack; \
- Except_stack = &except_frame; \
- except_flag = setjmp(except_frame.env); \
- if (except_flag == EXCEPT_ENTERED) \
- {
- //如果和刚刚压入Except_stack中的except_frame对应的longjmp不发生,
- //就将其从栈里面弹出来,而如果发生的话,就恢复这个环境变量所
- //保存的环境,回到setjmp()的位置重新进行处理,这时候except_flag
- //的值为EXCEPT_RAISED
- #define catch(e) \
- if(except_flag == EXCEPT_ENTERED) \
- Except_stack = Except_stack->prev; \
- }else if(except_frame.exception == &(e)){ \
- except_flag = EXCEPT_HANDLED;
- #define try_return \
- switch(Except_stack = Except_stack->prev,0) \
- default: return
- #define catch_else \
- if(except_flag == EXCEPT_ENTERED) \
- Except_stack = Except_stack->prev; \
- }else{ \
- except_flag = EXCEPT_HANDLED;
- //如果没有相关的处理函数,重新抛出
- #define end_try \
- if(except_flag == EXCEPT_ENTERED) \
- Except_stack = Except_stack->prev; \
- } \
- if (except_flag == EXCEPT_RAISED) \
- except_raise(except_frame.exception, \
- except_frame.file,except_frame.line); \
- }while(0)
- #define finally \
- if(except_flag == EXCEPT_ENTERED) \
- Except_stack = Except_stack->prev; \
- }{ \
- if(except_flag == EXCEPT_ENTERED) \
- except_flag = EXCEPT_FINALIZED;
- #undef T
- #endif
文件:exception.c
- #include "exception.h"
- Except_frame *Except_stack = NULL;
- void except_raise(const Except_t *e,const char *file,int line)
- {
- Except_frame *p = Except_stack;
- assert(e);
- if(p == NULL){
- abort_without_exception(e,file,line);
- }
- p->exception = e;
- p->file = file;
- p->line = line;
- Except_stack = Except_stack->prev;
- longjmp(p->env,EXCEPT_RAISED);
- }
- void abort_without_exception(const Except_t *e,const char *file,int line)
- {
- fprintf(stderr,"Uncaught exception");
- if(e->reason)
- fprintf(stderr," %s",e->reason);
- else
- fprintf(stderr," at 0x%p",e);
- if (file && line > 0)
- fprintf(stderr, "raised at %s:%d\n",file,line);
- fprintf(stderr,"aborting...\n");
- fflush(stderr);
- abort();
- }
参考资料:
《c语言接口与实现》 David R Hanson
2 楼 topxuchenghua 2011-01-10 13:50
1 楼 George_ghc 2010-09-30 09:31