- xombat
- 等级:
- 性别:
- 文章: 214
- 积分: 289
- 来自: 乌托邦
|
引子:
不管是在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 代码
-
-
- #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语句,因此下面的代码:
c代码
- try{
- S;
- }catch(e1){
- S1;
- }finally{
- S2;
- }end_try;
相当于java中的:
java 代码
- try{
- S;
- }catch(e1except e1){
- S1;
- }finally
- S2;
源代码
文件:exception.h
c 代码
- #ifndef __EXCEPTION_H__
- #define __EXCEPTION_H__
-
- #include <stdio.h></stdio.h>
- #include <setjmp.h></setjmp.h>
- #include <assert.h></assert.h>
-
-
- #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);
-
-
-
- #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) \
- {
-
-
-
-
-
- #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
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();
- }
参考资料:<o:p></o:p>
《c语言接口与实现》 David R Hanson<o:p></o:p>
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
返回顶楼 |
|
|
- jigsaw
- 等级: 初级会员
- 文章: 343
- 积分: 94
|
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.
|
返回顶楼 |
|
|
- xombat
- 等级:
- 性别:
- 文章: 214
- 积分: 289
- 来自: 乌托邦
|
如果s语句中存在一个i++操作,很可能在异常抛出后,i仍然为原值,除非i声明的时候是volatile。
这种异常处理机制很脆弱,但在大多数情况下已经够用。
|
返回顶楼 |
|
|
- zuowj
- 等级: 初级会员
- 性别:
- 文章: 18
- 积分: 92
- 来自: 北京
|
为什么要在C语言里业实现异常呢? 直接使用C++ 不行么?
|
返回顶楼 |
|
|
- xombat
- 等级:
- 性别:
- 文章: 214
- 积分: 289
- 来自: 乌托邦
|
比如嵌入式开发中由于开发环境不支持c++,只能使用c
开发底层东西,很多人都用c,环境所致你也得用c
---没有语言之争的意图
|
返回顶楼 |
|
|
- mathgl
- 等级: 初级会员
- 性别:
- 文章: 1102
- 积分: 50
- 来自: HK
|
可以看看 C interface and implimentation
里面的第四章 专门说这个
|
返回顶楼 |
|
|
- javaeye000
- 等级: 初级会员
- 文章: 25
- 积分: 30
- 来自: ...
|
用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。
|
返回顶楼 |
|
|
- xombat
- 等级:
- 性别:
- 文章: 214
- 积分: 289
- 来自: 乌托邦
|
javaeye000 写道 用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。 c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?
|
返回顶楼 |
|
|
- javaeye000
- 等级: 初级会员
- 文章: 25
- 积分: 30
- 来自: ...
|
xombat 写道 javaeye000 写道 用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。 c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?
不好意思,刚发现你的回复。我是指这个结构,里面的reason需要在抛出异常的地方给它分配空间,处理玩异常
之后再把它释放掉,这样也太麻烦了一点吧。
typedef struct Except_t{
char *reason;
}Except_t;
|
返回顶楼 |
|
|
- javaeye000
- 等级: 初级会员
- 文章: 25
- 积分: 30
- 来自: ...
|
xombat 写道 javaeye000 写道 用一个还要分配空间的结构变量表示抛出的异常也太麻烦了一点吧。 c++中的异常结构我还没去研究,不知道那是怎么处理的.
ls的想法是怎样的,不分配空间环境变量如何恢复?
不好意思,刚发现你的回复。我是指这个结构,里面的reason需要在抛出异常的地方给它分配空间,处理玩异常
之后再把它释放掉,这样也太麻烦了一点吧。
typedef struct Except_t{
char *reason;
}Except_t;
|
返回顶楼 |
|
|