您的位置:首页 > 编程语言 > C语言/C++

C语言实现异常处理(转)

2015-07-18 14:49 453 查看
起因:在c语言工程代码当中,常常要处理各种关于异常的处理,每次当程序比较复杂的时候,就必须用好几个if来嵌套使用,比如

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++的错误处理异常的机制了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: