time 
设为首页】【收藏本站
当前位置: 主页 > 程序设计 > C\C++\VC > C++基础 > 第18集 玩转setjmp与longjmp

第18集 玩转setjmp与longjmp

时间:2009-12-04 22:56 点击:642次 字体:[ ]




通过上两篇文章中,对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 modify
it under the terms of the GNU General Public License as published by
the 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 of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU 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 modify
it under the terms of the GNU General Public License as published by
the 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 of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU 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 block
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

}

  到目前为止,大家是不是已经觉得,这个主人公阿愚封装的在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认识上的盲区。不要错过,继续到下一篇文章中去吧!



本文地址 : http://www.fengfly.com/plus/view-159889-1.html
标签: 错误 异常 捕获 printf catch
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码:
本栏分类