`
xombat
  • 浏览: 162420 次
  • 性别: Icon_minigender_1
  • 来自: 乌托邦
社区版块
存档分类
最新评论

为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>

 

分享到:
评论
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 way.

相关推荐

    Java与C语言混合开发技术

    此外,还介绍了异常处理机制的不同,Java使用try-catch语句来捕获异常,而C语言使用return语句将异常信息返回给调用者。 在方法方面,详细介绍了JNI接口的定义、C语言编写本地方法以及Java代码调用本地方法的步骤。...

    支持中文标志符的C--编译器

    增加异常处理机制:Entry(),Leave(),Throw();这个略微自豪的功能。 建立了调试机制:Trap 指令。这也是一些不错的机制。 增加对宽字符的支持,并实现中文标志符编程. 保证能够实现自我编译(四次递归编译) 在 tcc 环境下...

    嵌入式软件中基于栈的错误追踪机制设计

    本文针对嵌入式C语言的编程特点,设计了一种基于堆栈模式的错误追踪机制,并论述了其具体实现方法,包括错误代码定义、错误处理堆栈设计及错误异常的描述方法,给出了详细代码。本方法已经在多个实际嵌入式产品开发中...

    嵌入式系统/ARM技术中的嵌入式软件中基于栈的错误追踪机制设计

    本文针对嵌入式C语言的编程特点,设计了一种基于堆栈模式的错误追踪机制,并论述了其具体实现方法,包括错误代码定义、错误处理堆栈设计及错误异常的描述方法,给出了详细代码。本方法已经在多个实际嵌入式产品开发...

    基于JAVA的学生通讯录管理系统设计和实现[文献综述].doc

    Java的强类型机制、异常处理、废料的自动收集等是Java程 序健壮性的重要保证。对指针的丢弃是Java的明智选择。Java的安全检查机制使得Java 更具健壮性。 5、Java语言是安全的。Java通常被用在网络环境中,为此,Java...

    清华大学Linux操作系统原理与应用

    4.3.1 缺页异常处理程序 79 4.3.2 请求调页 81 4.3.3 写时复制 83 4.4 物理内存的分配与回收 83 4.4.1 伙伴算法 85 4.4.2 物理页面的分配 86 4.4.3 物理页面的回收 88 4.4.4 slab分配模式 89 4.4.5 内核空间非连续...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    6.3.3 MFC中的异常处理 255 6.4 处理内存分配错误 256 6.5 函数重载 257 6.5.1 函数重载的概念 258 6.5.2 引用类型和重载选择 260 6.5.3 何时重载函数 260 6.6 函数模板 261 6.7 使用decltype操作符 263 ...

    Java核心技术 卷Ⅰ:基础知识 【中文】(第八版)

    第11章讨论异常处理,即Java的健壮机制,它用于处理调试好的程序可能出现的意外的情 况。异常提供了一种将正常的处理代码与错误处理代码分开的有效手段。当然,即使程序包含 处理所有异常情况的功能,依然有可能...

    C++编程思想习题

    第17章 异常处理 17.1C语言的出错处理 17.2抛出异常 17.3异常捕获 17.3.1try块 17.3.2异常处理器 17.3.3异常规格说明 17.3.4更好的异常规格说明 17.3.5捕获所有异常 17.3.6异常的重新抛出 17.3.7未被捕获的异常 17.4...

    开题报告-基于JAVA的俄罗斯方块游戏设计与实现.doc

    C++是对C语言的扩充,扩充的绝大部分来自著名语言中的最佳特性:从SIMULA 67中吸取了类,从ALGOL 68中吸取了运算符一名多用、引用和在分程序中任何地方说明变量,综合了Ada的类属和 Clu的模块特点,从BCPL中吸取异常...

    计算机类基础问题

    查的重点主要有:java基本语法,多线程,异常处理,抽象类,匿名类,接口,MVC架构 ,设计模式,Servlet,Struts,Spring,J2EE。以下是我遇见过的面试问题: 1)transient和volatile是java关键字吗?(瞬联) 2)...

    python cookbook(第3版)

    第一章:数据结构和算法 1.1 解压序列赋值给多个变量 1.2 解压可迭代对象赋值给多个变量 1.3 保留最后N个元素 1.4 查找最大或最小的N个元素 ... 15.20 处理C语言中的可迭代对象 15.21 诊断分析代码错误

    JAVA面试题最全集

    50.JAVA语言如何进行异常处理,关键字:thorws,throw,try,catch,finally 51.Object类(或者其子类)的finalize()方法在什么情况下被调用? 52.一个“.java”原文件中是否可以包括多个类(不是内部类)? 53.掌握...

    自己动手写操作系统 pdf

    自己动手写操作系统在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用C语言和汇编语言编写出一个具备操作系统基本功能的操作系统框架。本书不同于其他的理论型书籍,而是提供给读者一个动手...

    自己动手写操作系统

    本书在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用C语言和汇编语言编写出一个具备操作系统基本功能的操作系统框架。本书不同于其他的理论型书籍,而是提供给读者一个动手实践的路线图。...

    linux 内核源代码分析

    1. 4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的汇编语言代码 第2章 存储管理 2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户...

    Java开发技术大全(500个源代码).

    示例描述:本章学习Java的异常处理。 demoException_1.java 异常示例1 demoException_2.java 异常示例2 demoException_3.java 异常示例3 demoException_4.java 异常示例4 demoException_5.java 异常示例5 ...

    传智播客扫地僧视频讲义源码

    本教程共分为5个部分,第一部分是C语言提高部分,第二部分为C++基础部分,第三部分为C++进阶部分,第四部分为C、C++及数据结构基础部分,第五部分为C_C++与设计模式基础,内容非常详细. 第一部分 C语言提高部分目录...

    Firebird数据库中文版

    Firebird还实现了域,字段级别的约束,视图,异常,规则和权限管理,更多的详细信息,请参考Firebird发布通知和参考手册。 硬件需求: Firebird可以工作最常见的硬件环境中,甚至非常差的硬件中,也能很好的工作,...

Global site tag (gtag.js) - Google Analytics