正文

第18集 玩转setjmp与longjmp 2006-01-22 13:31:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/mroske/9819.html

分享到:

  通过上两篇文章中,对setjmp与longjmp两个函数的深入研究与分析,相信大家已经和主人公阿愚一样,对C语言中提供的这种异常处理机制的使用方法了如指掌了。请不要骄傲和自满,让我们更上一层楼,彻底玩转setjmp与longjmp这两个函数。  不要忘记,前面我们得出过结论,C语言中提供的这种异常处理机制,与C++中的异常处理模型很相似。例如,可以定义出类似的try block(受到监控的代码);catch block(异常错误的处理模块);以及可以随时抛出的异常(throw语句)。所以说,我们可以通过一种非常有技巧的封装,来达到对setjmp和longjmp的使用方法(或者说语法规则),基本与C++中的语法一致。很有诱惑吧!  首先展示阿愚封装的在C语言环境中异常处理框架  1、首先是接口的头文件,主要采用“宏”技术!代码如下: /************************************************** author: 王胜祥 ** email: <mantx@21cn.com> ** date: 2005-03-07 ** version: ** filename: ceh.h **************************************************/ /******************************************************************** This file is part of CEH(Exception Handling in C Language). CEH is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 of the License, or(at your option) any later version. CEH is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.   注意:这个异常处理框架不支持线程安全,不能在多线程的程序环境下使用。如果您想在多线程的程序中使用它,您可以自己试着来继续完善这个框架模型。*********************************************************************/ #include <stdio.h>#include <signal.h>#include <setjmp.h>#include <stdlib.h>#include <float.h>#include <math.h>#include <string.h> /////////////////////////////////////////////////////* 与异常有关的结构体定义 */typedef struct _CEH_EXCEPTION {int err_type; /* 异常类型 */int err_code; /* 错误代码 */char err_msg[80]; /* 错误信息 */}CEH_EXCEPTION; /* 异常对象 */ typedef struct _CEH_ELEMENT {jmp_buf exec_status;CEH_EXCEPTION ex_info; struct _CEH_ELEMENT* next;} CEH_ELEMENT; /* 存储异常对象的链表元素 *///////////////////////////////////////////////////// /////////////////////////////////////////////////////* 内部接口定义,操纵维护链表数据结构 */extern void CEH_push(CEH_ELEMENT* ceh_element);extern CEH_ELEMENT* CEH_pop();extern CEH_ELEMENT* CEH_top();extern int CEH_isEmpty();//////////////////////////////////////////////////// /* 以下是外部接口的定义 *//////////////////////////////////////////////////////* 抛出异常 */extern void thrower(CEH_EXCEPTION* e); /* 抛出异常 (throw)a表示err_type b表示err_code c表示err_msg */#define throw(a, b, c) \{ \CEH_EXCEPTION ex; \memset(&ex, 0, sizeof(ex)); \ex.err_type = a; \ex.err_code = b; \strncpy(ex.err_msg, c, sizeof(c)); \thrower(&ex); \} /* 重新抛出原来的异常 (rethrow)*/#define rethrow thrower(ceh_ex_info)//////////////////////////////////////////////////// /////////////////////////////////////////////////////* 定义try block(受到监控的代码)*/#define try \{ \int ___ceh_b_catch_found, ___ceh_b_occur_exception; \CEH_ELEMENT ___ceh_element; \CEH_EXCEPTION* ceh_ex_info; \memset(&___ceh_element, 0, sizeof(___ceh_element)); \CEH_push(&___ceh_element); \ceh_ex_info = &___ceh_element.ex_info; \___ceh_b_catch_found = 0; \if (!(___ceh_b_occur_exception=setjmp(___ceh_element.exec_status))) \{ /* 定义catch block(异常错误的处理模块)catch表示捕获所有类型的异常*/#define catch \} \else \{ \CEH_pop(); \___ceh_b_catch_found = 1; /* end_try表示前面定义的try block和catch block结束 */#define end_try \} \{ \/* 没有执行到任何的catch块中 */ \if(!___ceh_b_catch_found) \{ \CEH_pop(); \/* 出现了异常,但没有捕获到任何异常 */ \if(___ceh_b_occur_exception) thrower(ceh_ex_info); \} \} \} /* 定义catch block(异常错误的处理模块)catch_part表示捕获一定范围内的异常*/#define catch_part(i, j) \} \else if(ceh_ex_info->err_type>=i && ceh_ex_info->err_type<=j) \{ \CEH_pop(); \___ceh_b_catch_found = 1; /* 定义catch block(异常错误的处理模块)catch_one表示只捕获一种类型的异常*/#define catch_one(i) \} \else if(ceh_ex_info->err_type==i) \{ \CEH_pop(); \___ceh_b_catch_found = 1;//////////////////////////////////////////////////// /////////////////////////////////////////////////////* 其它可选的接口定义 */extern void CEH_init();//////////////////////////////////////////////////// 2、另外还有一个简单的实现文件,主要实现功能封装。代码如下: /************************************************** author: 王胜祥 ** email: <mantx@21cn.com> ** date: 2005-03-07 ** version: ** filename: ceh.c * *************************************************/ /******************************************************************** This file is part of CEH(Exception Handling in C Language). CEH is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 of the License, or(at your option) any later version. CEH is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details. 注意:这个异常处理框架不支持线程安全,不能在多线程的程序环境下使用。如果您想在多线程的程序中使用它,您可以自己试着来继续完善这个框架模型。*********************************************************************/ #include "ceh.h" ////////////////////////////////////////////////////static CEH_ELEMENT* head = 0; /* 把一个异常插入到链表头中 */void CEH_push(CEH_ELEMENT* ceh_element){if(head) ceh_element->next = head;head = ceh_element;} /* 从链表头中,删除并返回一个异常 */CEH_ELEMENT* CEH_pop(){CEH_ELEMENT* ret = 0; ret = head;head = head->next; return ret;} /* 从链表头中,返回一个异常 */CEH_ELEMENT* CEH_top(){return head;} /* 链表中是否有任何异常 */int CEH_isEmpty(){return head==0;}//////////////////////////////////////////////////// /////////////////////////////////////////////////////* 缺省的异常处理模块 */static void CEH_uncaught_exception_handler(CEH_EXCEPTION *ceh_ex_info) {printf("捕获到一个未处理的异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);fprintf(stderr, "程序终止!\n");fflush(stderr);exit(EXIT_FAILURE); }//////////////////////////////////////////////////// /////////////////////////////////////////////////////* 抛出异常 */void thrower(CEH_EXCEPTION* e) {CEH_ELEMENT *se; if (CEH_isEmpty()) CEH_uncaught_exception_handler(e); se = CEH_top();se->ex_info.err_type = e->err_type;se->ex_info.err_code = e->err_code;strncpy(se->ex_info.err_msg, e->err_msg, sizeof(se->ex_info.err_msg)); longjmp(se->exec_status, 1);}//////////////////////////////////////////////////// ////////////////////////////////////////////////////static void fphandler( int sig, int num ){_fpreset(); switch( num ){case _FPE_INVALID:throw(-1, num, "Invalid number" );case _FPE_OVERFLOW:throw(-1, num, "Overflow" );case _FPE_UNDERFLOW:throw(-1, num, "Underflow" );case _FPE_ZERODIVIDE:throw(-1, num, "Divide by zero" );default:throw(-1, num, "Other floating point error" );}} void CEH_init(){_control87( 0, _MCW_EM );if( signal( SIGFPE, fphandler ) == SIG_ERR ){fprintf( stderr, "Couldn't set SIGFPE\n" );abort(); }}////////////////////////////////////////////////////  体验上面设计出的异常处理框架请花点时间仔细揣摩一下上面设计出的异常处理框架。呵呵!程序员朋友们,大家是不是发现它与C++提供的异常处理模型非常相似。例如,它提供的基本接口有try、catch、以及throw等三条语句。还是先看个具体例子吧!以便验证一下这个C语言环境中异常处理框架是否真的比较好用。代码如下: #include "ceh.h" int main(void) {//定义try block块try{int i,j;printf("异常出现前\n\n");// 抛出一个异常// 其中第一个参数,表示异常类型;第二个参数表示错误代码// 第三个参数表示错误信息throw(9, 15, "出现某某异常"); printf("异常出现后\n\n");}//定义catch block块catch{printf("catch块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}// 这里稍有不同,需要定义一个表示当前的try block结束语句// 它主要是清除相应的资源end_try}   注意,上面的测试程序可是C语言环境下的程序(文件的扩展名请使用.c结尾),虽然它看上去很像C++程序。请编译运行一下,发现它是不是运行结果如下:异常出现前 catch块,被执行到  捕获到一个异常,错误原因是:出现某某异常! err_type:9 err_code:15   呵呵!程序的确是在按照我们预想的流程在执行。再次提醒,这可是C程序,但是它的异常处理却非常类似于C++中的风格,要知道,做到这一点其实非常地不容易。当然,上面异常对象的传递只是在一个函数的内部,同样,它也适用于多个嵌套函数间的异常传递,还是用代码验证一下吧!在上面的代码基础下,小小修改一点,代码如下: #include "ceh.h" void test1(){throw(0, 20, "hahaha");} void test(){test1();} int main(void) {try{int i,j;printf("异常出现前\n\n");// 注意,这个函数的内部会抛出一个异常。test(); throw(9, 15, "出现某某异常"); printf("异常出现后\n\n");}catch{printf("catch块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try}   同样,在上面程序中,test1()函数内抛出的异常,可以被上层main()函数中的catch block中捕获到。运行结果就不再给出了,大家可以自己编译运行一把,看看运行结果。另外这个异常处理框架,与C++中的异常处理模型类似,它也支持try catch块的多层嵌套。很厉害吧!还是看演示代码吧!,如下: #include "ceh.h" int main(void) {// 外层的try catch块try{// 内层的try catch块try{throw(1, 15, "嵌套在try块中");}catch{printf("内层的catch块被执行\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code); printf("外层的catch块被执行\n");}end_try throw(2, 30, "再抛一个异常");}catch{printf("外层的catch块被执行\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try}   请编译运行一下,程序的运行结果如下:  内层的catch块被执行  捕获到一个异常,错误原因是:嵌套在try块中! err_type:1 err_code:15  外层的catch块被执行  捕获到一个异常,错误原因是:再抛一个异常! err_type:2 err_code:30   还有,这个异常处理框架也支持对异常的分类处理。这一点,也完全是模仿C++中的异常处理模型。不过,由于C语言中,不支持函数名重载,所以语法上略有不同,还是看演示代码吧!,如下: #include "ceh.h" int main(void) {try{int i,j;printf("异常出现前\n\n");throw(9, 15, "出现某某异常"); printf("异常出现后\n\n");}// 这里表示捕获异常类型从4到6的异常catch_part(4, 6){printf("catch_part(4, 6)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}// 这里表示捕获异常类型从9到10的异常catch_part(9, 10){printf("catch_part(9, 10)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}// 这里表示只捕获异常类型为1的异常catch_one(1){printf("catch_one(1)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}// 这里表示捕获所有类型的异常catch{printf("catch块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try}   请编译运行一下,程序的运行结果如下:  异常出现前 catch_part(9, 10)块,被执行到  捕获到一个异常,错误原因是:出现某某异常! err_type:9 err_code:15   与C++中的异常处理模型相似,它这里的对异常的分类处理不仅支持一维线性的;同样,它也支持分层的,也即在当前的try catch块中找不到相应的catch block,那么它将会到上一层的try catch块中继续寻找。演示代码如下: #include "ceh.h" int main(void) {try{try{throw(1, 15, "嵌套在try块中");}catch_part(4, 6){printf("catch_part(4, 6)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try printf("这里将不会被执行到\n");}catch_part(2, 3){printf("catch_part(2, 3)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}// 找到了对应的catch blockcatch_one(1){printf("catch_one(1)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}catch{printf("catch块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try }   到目前为止,大家是不是已经觉得,这个主人公阿愚封装的在C语言环境中异常处理框架,已经与C++中的异常处理模型95%相似。无论是它的语法结构;还是所完成的功能;以及它使用上的灵活性等。下面我们来看一个各种情况综合的例子吧!代码如下: #include "ceh.h" void test1(){throw(0, 20, "hahaha");} void test(){test1();} int main(void) {try{test();}catch{printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try try{try{throw(1, 15, "嵌套在try块中");}catch{printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try throw(2, 30, "再抛一个异常");}catch{printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code); try{throw(0, 20, "嵌套在catch块中");}catch{printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}end_try}end_try}   请编译运行一下,程序的运行结果如下:  捕获到一个异常,错误原因是:hahaha! err_type:0 err_code:20  捕获到一个异常,错误原因是:嵌套在try块中! err_type:1 err_code:15  捕获到一个异常,错误原因是:再抛一个异常! err_type:2 err_code:30  捕获到一个异常,错误原因是:嵌套在catch块中! err_type:0 err_code:20   最后,为了体会到这个异常处理框架,更进一步与C++中的异常处理模型相似。那就是它还支持异常的重新抛出,以及系统中能捕获并处理程序中没有catch到的异常。看代码吧!如下: #include "ceh.h" void test1(){throw(0, 20, "hahaha");} void test(){test1();} int main(void) {// 这里表示程序中将捕获浮点数计算异常CEH_init(); try{try{try{double i,j;j = 0;// 这里出现浮点数计算异常i = 1/j ;test(); throw(9, 15, "出现某某异常");}end_try}catch_part(4, 6){printf("catch_part(4, 6)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}catch_part(2, 3){printf("catch_part(2, 3)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);}// 捕获到上面的异常catch{printf("内层的catch块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code); // 这里再次把上面的异常重新抛出rethrow; printf("这里将不会被执行到\n");}end_try}catch_part(7, 9){printf("catch_part(7, 9)块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code); throw(2, 15, "出现某某异常");}// 再次捕获到上面的异常catch{printf("外层的catch块,被执行到\n");printf("捕获到一个异常,错误原因是:%s! err_type:%d err_code:%d\n",ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code); // 最后又抛出了一个异常,// 但是这个异常没有对应的catch block处理,所以系统中处理了throw(2, 15, "出现某某异常");}end_try}   请编译运行一下,程序的运行结果如下:  内层的catch块,被执行到  捕获到一个异常,错误原因是:Divide by zero! err_type:-1 err_code:131  外层的catch块,被执行到  捕获到一个异常,错误原因是:Divide by zero! err_type:-1 err_code:131  捕获到一个未处理的异常,错误原因是:出现某某异常! err_type:2 err_code:15  程序终止!总结  主人公阿愚封装的这个在C语言环境中的异常处理框架,是完全建立在setjmp与longjmp的机制之上的。它的目的使我们对setjmp和longjmp的使用起来更方便和友善;同时,也更加深我们对setjmp和longjmp的了解;另外,还有一个重要的目的,就是给我们大家自己一个机会,来思考C++中异常处理模型的实现,当然后者会更复杂,但是这里将是一个预习课,它对后面真正阐述C++中异常处理模型的实现将大有帮助。  另外,这个异常处理框架不支持线程安全,不能在多线程的程序环境下使用。如果您想在多线程的程序中使用它,您可以自己试着来继续完善这个框架模型。当然,您也可以给主人公阿愚发email(mantx@21cn.com),来共同探讨合理的解决方案。  下一节将继续讨论setjmp与longjmp在C++中的应用情况,这很关键呦!是许多程序员朋友对setjmp与longjmp认识上的盲区。不要错过,继续到下一篇文章中去吧!

阅读(3407) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册