C语言实现异常处理(转)
2015-07-18 14:49
453 查看
起因:在c语言工程代码当中,常常要处理各种关于异常的处理,每次当程序比较复杂的时候,就必须用好几个if来嵌套使用,比如
这样的代码显得非常混乱,也不容易管理,我一直在寻找能跟c++异常机制类似的功能,如果有这样的功能,那么c语言的异常处理不是也很容易打理了么?
由于c的工程当中一般错误都有专有的错误列表,所以在这边,在我们的机制里我只添加了关于错误Id的异常捕捉处理。
思路:
首先,我使用了#define来将c当中没有的关键词替代,比如try,catch,throw,先想到的是用return,break之类的c语言当中先有的关键词来模拟,这就有了我的第一个版本:
调用实例代码:
这个设计的程序有一个很大的问题,那就是它只能在同级的函数下使用try。
这是什么意思呢?如果我需要这样使用:
在g()throw出异常3的时候,main函数没有办法直接捕捉,而是必须在f()函数下面继续执行后面的语句。就是这套设计的机制多个嵌套的函数是没有办法实施的。
那么还有什么办法可以解决这个问题呢?Goto可以吗?不可以,goto也只能在同级的函数下使用。goto是本地的:它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点 。
最后找到一个方法:
setjmp 和longjmp这2个函数可以在不同的函数进行跳转,只要设置他们的上下文,在用longjmp跳转到这设置好的上下文当中就可以了!!
关于这里的setjmp和longjmp, 在main函数中第一次设置好setjmp后, 其返回值为0; 在longjmp跳转到setjmp后, setjmp的返回值则为longjmp的第二个参数的值, 即不再为0
这2个函数的使用方法:
有了这个方法,我们的程序就好设计多了,由于设计多个函数的调用,所以设计的时候使用了链表来处理保存每个函数的env。
全部实现代码如下:
这边,List.h和list.c摘抄自linux内核代码。
使用demo代码:
这样,就可以实现一个简易版本的用c语言模拟c++的错误处理异常的机制了。
if (xxx) { if(xxx) { } else { return ERR_NO; } }
这样的代码显得非常混乱,也不容易管理,我一直在寻找能跟c++异常机制类似的功能,如果有这样的功能,那么c语言的异常处理不是也很容易打理了么?
由于c的工程当中一般错误都有专有的错误列表,所以在这边,在我们的机制里我只添加了关于错误Id的异常捕捉处理。
思路:
首先,我使用了#define来将c当中没有的关键词替代,比如try,catch,throw,先想到的是用return,break之类的c语言当中先有的关键词来模拟,这就有了我的第一个版本:
#ifndef EXCEPTION_H #define EXCEPTION_H // 一个不成熟的exception机制 typedef unsigned int ERR_TYPE; #define NO_ERR 0 static ERR_TYPE err; //try-catch-throw错误处理机制 #define try do #define catch(N) while(0);if(N>=NO_ERR) #define throw(N) err=N;break; #define throwAgain(N) err=N;return; #endif
调用实例代码:
void f(void) { try { throw(6); } catch(err) { throwAgain(err); } } int main() { try { f(); } catch(err) { printf("%d", err); } }
这个设计的程序有一个很大的问题,那就是它只能在同级的函数下使用try。
这是什么意思呢?如果我需要这样使用:
Int main() { Try { f(); } catch(err) { printf("%d", err); } } Void f() { G(); // some codes follows.. } Void g() { Throw(3); }
在g()throw出异常3的时候,main函数没有办法直接捕捉,而是必须在f()函数下面继续执行后面的语句。就是这套设计的机制多个嵌套的函数是没有办法实施的。
那么还有什么办法可以解决这个问题呢?Goto可以吗?不可以,goto也只能在同级的函数下使用。goto是本地的:它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点 。
最后找到一个方法:
#include <setjmp.h> int setjmp(jmp_buf env) Returns: 0 if called directly, nonzero if returning from a call to longjmp. void longjmp(jmp_buf env, int val);
setjmp 和longjmp这2个函数可以在不同的函数进行跳转,只要设置他们的上下文,在用longjmp跳转到这设置好的上下文当中就可以了!!
关于这里的setjmp和longjmp, 在main函数中第一次设置好setjmp后, 其返回值为0; 在longjmp跳转到setjmp后, setjmp的返回值则为longjmp的第二个参数的值, 即不再为0
这2个函数的使用方法:
#include<stdio.h> #include<setjmp.h> jmp_buf ebuf; void f2(void); int main(void) { int i; printf("1"); i=setjmp(ebuf); if(i==0) { f2(); printf("This will not be printed."); } printf("%d",i); return 0; } void f2(void) { printf("2"); longjmp(ebuf,3); }首先,在函数a里面setjmp,将当前函数的env都存储起来,然后在函数b里面调用longjmp,这样就会直接跳转到函数a的setjmp这个地方,longjmp的第二个值作用是在第二次返回a函数的时候setjmp会返回longjmp的第二个参数。
有了这个方法,我们的程序就好设计多了,由于设计多个函数的调用,所以设计的时候使用了链表来处理保存每个函数的env。
全部实现代码如下:
#ifndef LIST_H #define LIST_H typedef struct list_head_tag { struct list_head_tag *prev; struct list_head_tag *next; }list_head; #define list_for_each(pos, head) \ for ( pos = (head)->next; \ pos != (head); \ pos = pos->next) #define list_for_each_safe(pos,pos_next,head) \ for ( pos = (head)->next, pos_next = pos->next; \ pos != (head); \ pos = pos_next, pos_next = pos->next) #define list_empty(head) (head->next == head) #define list_entry(ptr, type, member) ((type*)ptr) #define init_list_head(ptr) \ do{\ (ptr)->prev = ptr; \ (ptr)->next = ptr; \ }while(0) extern void list_add_before(list_head *node, list_head *pos); extern void list_add_after(list_head *node, list_head *pos); extern void list_del(list_head *node); #endif
#include "list.h" void list_add_before(list_head *node, list_head *pos) { node->prev = pos->prev; node->next = pos; pos->prev->next = node; pos->prev = node; } void list_add_after(list_head *node, list_head *pos) { node->prev = pos; node->next = pos->next; pos->next->prev = node; pos->next = node; } void list_del(list_head *node) { node->prev->next = node->next; node->next->prev = node->prev; }
#ifndef EXC_H #define EXC_H char err = -1; static char isJumpListInit = 0; //jmp_buf jump_buffer; typedef struct JumpBufListTag { struct list_head_tag list; jmp_buf jump_buffer; }JumpBufList, *JumpBufListPtr; JumpBufList jumplist = {NULL, NULL}; JumpBufListPtr head = &jumplist; JumpBufListPtr cur = &jumplist; int SetCurJump(void) { JumpBufListPtr newPtr = (JumpBufList*)calloc(sizeof(JumpBufList)); if (!isJumpListInit) { init_list_head(&head->list); isJumpListInit = 1; } list_add_after(&newPtr->list, &head->list); cur = newPtr; return 0; } void JumpCurLong(void) { longjmp(cur->jump_buffer, 1); } void DestoryCurJumpEnv( void ) { list_del(&cur->list); free(cur); cur = head->list.next; } #define try SetCurJump();if(setjmp(cur->jump_buffer) == 0) #define catch(N) DestoryCurJumpEnv();if(N>=0) #define throw(N) err=N;JumpCurLong(); #endif
这边,List.h和list.c摘抄自linux内核代码。
使用demo代码:
void h() { throw(7); } void e() { h(); } void g(void) { try { e(); printf("g()3"); } catch(err) { throw(err); } } int main() { try { g(); } catch(err) { printf("%d", err); } return 0; }
这样,就可以实现一个简易版本的用c语言模拟c++的错误处理异常的机制了。
相关文章推荐
- 算法导论 第十九章:斐波拉契堆
- MFC(VC++或VS)中使用Halocn
- 【C++ Primer】拷贝控制
- C++中overload,override,overwrite的区别详细解析
- C++ Primer笔记10_运算符重载_赋值运算符_进入/输出操作符
- 一起talk C栗子吧(第十九回:C语言实例--位操作)
- C++项目实训:银行储蓄系统
- C/C++程序设计06(面向对象)
- c++Builder 2009 2010 出现Cannot convert 'wchar_t *' to 'const char *
- C语言中内存分布及程序运行中(BSS段、数据段、代码段、堆栈)
- C语言简单实现计算字符个数的方法
- C++内存管理
- C/C++中volatile的用法
- C/C++中volatile的用法
- C语言基础4
- B树、B-树、B+树、B*树
- [c++学习笔记]反汇编角度看变量名和引用作为函数参数
- 算法导论 第十八章;B 树
- 《C++ Primer》 ---- 关于变量 与 基本类型
- C语言中如何将二维数组作为函数的参数传递