关于-异常处理

概述

什么是异常?异常一般指的是程序运行期(Run-Time)发生的非正常情况。异常一般是不可预测的,如:内存不足、打开文件失败、范围溢出等。UNIX 使用信号给出异常,并当发生异常时转跳到信号处理过程进行异常处理。DOS下的信号对比UNIX系统而言相对较少。

我们知道,不管是在c++还是在java中,异常都被认为是一种很优雅的处理错误的机制。而如果想在c语言中使用异常就比较麻烦,但是我们仍然可以使用c语言中强大的setjmp和longjmp函数实现类似于c++的异常处理机制。

异常处理的核心思想是,把功能模块代码与系统中可能出现错误的处理代码分离开来,以此来达到使我们的代码组织起来更美观、逻辑上更清晰,并且同时从根本上来提高我们软件系统长时间稳定运行的可靠性。那么,现在回过头来看,实际上在计算机系统的硬件设计中,操作系统的总体设计中,早期的许多面向结构化程序设计语言中(例如C语言),都有异常处理的机制和方法的广泛运用。

异常处理机制:setjmp()函数与longjmp()函数

C标准库提供两个特殊的函数:setjmp() 及 longjmp(),这两个函数是结构化异常的基础,正是利用这两个函数的特性来实现异常。
所以,异常的处理过程可以描述为这样:
首先设置一个跳转点(setjmp() 函数可以实现这一功能),然后在其后的代码中任意地方调用 longjmp() 跳转回这个跳转点上,以此来实现当发生异常时,转到处理异常的程序上,在其后的介绍中将介绍如何实现。
setjmp() 为跳转返回保存现场并为异常提供处理程序,longjmp() 则进行跳转(抛出异常),setjmp() 与 longjmp() 可以在函数间进行跳转,这就像一个全局的 goto 语句,可以跨函数跳转。
举个例子,程序在 main() 函数内使用 setjmp() 设置跳转,并调用另一函数A,函数A内调用B,B抛出异常(调用longjmp() 函数),则程序直接跳回到 main() 函数内使用 setjmp() 的地方返回,并且返回一个值。

异常处理过程

#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

setjmp()第一次调用时总是返回0,而通过longjmp(env,val跳转后其返回值总是被修改为val,并且val不能为0。这样程序中就很容易根据setjmp()的返回值来判断是否是longjmp()导致了跳转才执行到此。

代码实例

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

jmp_buf Jump_Buffer;


double Divide(double a, double b)
{
    if (b == 0.0)
    {
        longjmp(Jump_Buffer, 1);        // throw
    }
    else
        return a / b;
}

int main(int argc, const char *argv[])
{
	int ret;
    ret = setjmp(Jump_Buffer);
    if (ret == 0)           // try
    {
        printf("division ...\n");
        printf("%f\n", Divide(5.0, 0.0));
    }
    else if (ret == 1)      // catch
    {
        printf("divisiong by zero\n");
    }
	
	return 0;
}

宏定义升级改造

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

jmp_buf Jump_Buffer;
#define try if(!setjmp(Jump_Buffer))
#define catch   else
#define throw longjmp(Jump_Buffer,1)

double Divide(double a, double b)
{
    if (b == 0.0)
    {
       throw;    // throw
    }
    else
        return a / b;
}

int main(int argc, const char *argv[])
{
	int ret;
    ret = setjmp(Jump_Buffer);
	try{ // try
		
		printf("division ...\n");
        printf("%f\n", Divide(5.0, 0.0));
		
	}
    catch { // catch
        printf("divisiong by zero\n");
    }
	
	return 0;
}

如此看起来便跟 C++ 相似了
首先定义一个全局的异常:

jmp_buf Jump_Buffer;

因为 setjmp() 第一次调用初始化后返回0,第二次返回非0,可以这样定义一个宏使得它功能接近于 C++ 的 try。

#define try if(!setjmp(Jump_Buffer))

当 setjmp() 函数第一次0 时,取非为真,则执行 try 块内的代码,如:

try {

Test();

}

当因为调用 longjmp() 抛出异常而导致 setjmp() 第二次返回时(程序将会转到 setjmp() 函数处返回,这时,这时应该执行的是异常处理代码。longjmp() 使 setjmp() 函数返回非0,if(!setjmp(JumpBuffer)) 中将值取非则为假,是以,异常处理放在其后应该使用一个 else:

#define catch else

如此看起来便跟 C++ 相似了,setjmp() 函数的第二次返回导致 if() 中表达式值为假,刚好使 catch 块得以执行,如:

try  {

Test();

} catch {

puts("Error");

}

实现如 C++ 的 throw 语句,事实上以宏替换 longjmp(jmp_buf, int) 的调用:

#define throw longjmp(Jump_Buffer, 1)

参考

https://blog.csdn.net/tian_dao_chou_qin/article/details/6386621

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值