中raise抛出异常_C语言拾遗:使用setjmp实现异常机制

之前总结了一下 setjmp 的使用:

JuJu:C语言拾遗:setjmp​zhuanlan.zhihu.com

我个人在开发中实际上没用过 setjmp 这个东西,但它毕竟存在在那里。C语言标准库的函数用指头数的过来,所以想搞明白它。

这篇笔记打算总结一下,怎么在C语言中实现异常。

异常是一个运行时错误,在C语言中,可以用assert来处理运行时错误,但它会直接abort掉程序。

面对异常,可以有三种选择:

一种是不管它,那么它就会abort程序。

一种是捕获它,然后做特殊处理。

还有一种是在异常处理代码中重新抛出它,让更外层的异常处理代码处理它。

比如:

RAISE(Mem_failed);

这里直接抛了一个异常对象 Mem_failed ,没有异常处理代码,那么程序输出错误信息,abort。

又比如:

TRY {
    RAISE(Mem_failed);
}
EXCEPT(Mem_failed) {
    /* catch */
}
END_TRY;

这里捕获了异常对象 Mem_failed ,那么程序继续往下执行。

异常对象可以简单地定义成一个全局的对象:

typedef struct ExceptObj {
    const char* name;
} ExceptObj ;

ExceptObj Mem_failed = { "Out of memory" };

TRY是代码的起始点,在RAISE一个异常的时候,要跳回此处。用一个数据结构表示它,起名叫异常帧:

typedef struct ExceptFrame {
    jmp_buf env;
    ExceptObj *except;
    const char *file;
    int line;
    int status;
} ExceptFrame;

env 是setjmp 和longjmp使用的,status是longjmp的返回值,从而表达一个异常情况,取值范围定义成:

enum { EXCEPT_ENTER, EXCEPT_RAISE, EXCEPT_HANDLE };

file 和 line 在RAISE异常时,被设置成此时的文件名和行号。

现在代码可以写成这样:

ExceptFrame frame;
frame.status = setjmp(frame.env);
if (frame.status == EXCEPT_ENTER) {
    // our normal code, raise an exception
    frame.file = __FILE__;
    frame.line = __LINE__;
    frame.except = &Mem_failed;
    longjmp(frame.env, EXCEPT_RAISE);
}
else if (&Mem_failed == frame.except) {
    frame.status = EXCEPT_HANDLE;
    // our handle code
}

// uncaught, abort
if (frame.status == EXCEPT_RAISE) {
    fprintf(stderr, "an exception %s occured at %s:%dn", frame.except->name, frame.file, frame.line);
    fflush(stderr);
    abort();
}

else if 分支捕获了异常,并设置好了处理状态。如果没有,那么最后就会被abort。

完整的测试代码如:

#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>

enum { EXCEPT_ENTER, EXCEPT_RAISE, EXCEPT_HANDLE };

typedef struct ExceptObj {
    const char* name;
} ExceptObj ;

ExceptObj Mem_failed = { "Out of memory" };

typedef struct ExceptFrame {
    jmp_buf env;
    ExceptObj *except;
    const char *file;
    int line;
    int status;
} ExceptFrame;

int main()
{
    ExceptFrame frame;
    frame.status = setjmp(frame.env);
    if (frame.status == EXCEPT_ENTER) {
        // our normal code, raise an exception
        frame.file = __FILE__;
        frame.line = __LINE__;
        frame.except = &Mem_failed;
        longjmp(frame.env, EXCEPT_RAISE);
    }   
    /*else if (&Mem_failed == frame.except) {
        frame.status = EXCEPT_HANDLE;
        // our handle code
    }*/

    // uncaught, abort
    if (frame.status == EXCEPT_RAISE) {
        fprintf(stderr, "an exception %s occured at %s:%dn", frame.except->name, frame.file, frame.line);
        fflush(stderr);
        abort();
    }   

    printf("Happy enddingn");

    return 0;
}

但这样的处理只能是玩具,没有实用价值,因为代码编写的很繁琐,而且不能 RERAISE ,也没有清理机制。

这里有一个比较完善的代码可供参考,由于使用了宏造成了大量扭曲代码,就不做相关笔记了。

demon90s/CLib​github.com
ba67ffd8868c6ca3af0d8bc217ff392a.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值