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

起因:在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++的错误处理异常的机制了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值