`
xombat
  • 浏览: 163684 次
  • 性别: 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.

相关推荐

    使用纯C语言实现异常处理

    然而,C语言本身并不直接支持异常处理机制,它依赖于错误返回码和自定义错误处理函数来处理异常情况。但在某些情况下,开发者可能需要在C语言中实现类似异常处理的功能,以增强代码的健壮性和可维护性。本篇文章将...

    可嵌套的C语言异常处理机制

    为了实现可嵌套的C语言异常处理机制,开发者通常会采用自定义的错误处理策略或者利用编译器扩展。下面我们将详细探讨这一主题。 异常处理是程序在遇到错误或异常情况时,能够优雅地捕获并处理问题的一种机制。在...

    用C语言实现的异常处理库

    然而,通过自定义的库函数,我们可以模拟实现类似于C++或Java的异常处理机制。这个“用C语言实现的异常处理库”就是这样一个尝试,它允许程序员在C代码中优雅地处理错误情况,而无需使用复杂的错误检查和返回值解析...

    C语言的异常处理和C++的异常处理有何区别?

    1. 异常安全:C++的异常处理机制支持异常安全编程,即在异常发生时能够正确地清理资源,避免内存泄漏。 2. 异常无关性:异常不会影响正常流程的控制流,使得代码更加清晰。 3. 多态性:`catch`块可以捕获基类类型的...

    ssdp协议c语言实现

    通过以上步骤,可以构建一个基本的SSDP协议C语言实现,为设备和服务的自动发现提供基础支持。然而,为了满足实际应用的需求,可能还需要扩展功能,如支持更多类型的SSDP消息、增加日志记录、完善配置选项等。

    C语言实现的websocket

    - 错误处理:良好的错误处理机制是必须的,确保在遇到异常情况时能够妥善关闭连接,防止资源泄露。 - 网络编程特性:理解TCP连接的异步性质,处理好阻塞和非阻塞IO模式。 - 性能优化:考虑使用多线程或异步I/O来...

    Hmac算法c语言实现

    哈希消息认证码(HMAC,Hash-based ...总之,HMAC算法在C语言中的实现涉及对基础哈希函数的理解,以及对密钥和消息的适当处理。通过精心设计的代码,可以在保证安全性和效率的同时,实现对数据完整性和来源的有效验证。

    C语言异常封装 模仿try catch

    在C语言中,由于其自身并不支持像Java那样的异常处理机制(try-catch),因此开发者通常需要通过自定义的方式来实现类似的功能。这个压缩包文件"**C异常封装**"很可能包含了一个C语言编写的示例代码,展示了如何模仿...

    数字信号处理算法c语言实现

    3. 错误处理:良好的错误处理机制可以确保程序在遇到异常情况时能正常退出,而不是产生未定义的行为。 4. 测试与验证:使用仿真数据和实际信号进行测试,确保算法的正确性。可以利用MATLAB或Python等工具进行快速...

    电力系统潮流计算的C语言实现

    C语言的错误处理机制可以捕获并处理这些异常情况,保证程序的稳健性。 10. **优化与并行计算**:对于大规模电力系统,可以利用C语言的多线程或OpenMP等并行计算库,提高潮流计算的效率。 综上所述,"电力系统潮流...

    经典算法 C语言实现

    5. **错误处理**:在实现过程中,需要考虑各种边界条件和异常情况,避免程序出错或崩溃。 6. **调试技巧**:学会使用`printf`进行调试,或者利用GDB等调试工具,对算法的执行过程进行跟踪,找出潜在的问题。 7. **...

    性的最有效方法之一C语言实现出错处理的方法是.ppt

    尽管C++的异常处理机制在功能和灵活性方面较C语言有显著优势,但在C语言中,开发者仍然可以通过一些策略来实现有效的错误处理。例如,使用errno全局变量和perror()、strerror()函数可以获取更多关于错误的详细信息。...

    c语言实现的象棋源码

    通过使用`if`语句和异常处理机制,我们可以确保程序在遇到问题时能优雅地处理。 7. **编译与调试**:C语言的项目需要通过编译器转化为可执行文件。使用如GCC这样的编译器,配合调试工具如GDB,可以帮助我们找出并...

    C语言实现ota升级代码

    C语言中的异常处理和状态机设计模式可以帮助构建这样的机制。 7. **安全性**:由于OTA涉及到固件的修改,所以安全性是至关重要的。固件签名可以用来验证软件包的来源和完整性,防止恶意篡改。C语言中可以使用加密库...

    C语言实现电梯控制

    8. **错误处理**:编写健壮的代码需要考虑各种异常情况,如无效的楼层请求、电梯满载、系统故障等,都需要适当的错误处理机制。 9. **资源管理**:电梯系统可能会有电源管理、电梯维护等需求,需要在代码中体现这些...

    onenet对接token计算C语言实现

    8. **错误处理**:编写健壮的代码,包含适当的错误检查和异常处理,以确保在遇到问题时能够适当地反馈和恢复。 9. **安全性**:理解网络安全原则,确保token的生成和传输过程中不会泄露敏感信息。 在压缩包文件...

    C语言的那些小秘密之异常处理

    今天,我们将讨论C语言中的异常处理,特别是使用setjmp()和longjmp()函数实现异常处理。 异常处理的定义 ---------------- 异常处理是一种机制,它允许程序在出现错误或异常情况时能够继续执行,而不是崩溃或退出...

    C语言实现JSON的解码与编码

    因此,良好的错误处理机制是必要的,包括返回错误码、抛出异常或者设置错误信息等。 通过这些知识点,开发者可以在C语言环境中高效地处理JSON数据,无论是从服务器接收还是向服务器发送,都能游刃有余。同时,理解...

    用C语言实现的计算器(支持复杂运算)

    因此,良好的错误处理机制是必不可少的,例如使用异常处理或返回错误代码。 8. **栈数据结构**:为了正确处理复杂的运算,例如括号内的表达式,可以使用栈来保存待计算的运算符和操作数。栈的特性“后进先出”...

    Linux下线程池的C语言实现

    - **异常处理**:处理线程执行过程中可能出现的各种异常情况,如资源不足、任务执行失败等。 总之,Linux下线程池的C语言实现是一个复杂但充满挑战的过程,它不仅考验着开发者的编程技巧,也对其系统设计和资源管理...

Global site tag (gtag.js) - Google Analytics