我们今天说说一种典型的实现C语言异常处理的方法,即以setjmp()函数和longjmp()函数实现的异常处理。
首先我们来了解下什么是异常处理。异常是一个在程序执行期间发生的事件,它中断正在执行的程序的正常的指令流,跳转到异常处理函数中,待异常处理完再跳回到正常的指令流中去。
我们需要利用两个系统函数,由你自己确定异常处理后返回的地方。接下来我们先看看这两个函数(setjmp()函数和longjmp()函数)是如何实现C语言异常处理。
int setjmp(jmp_buf env)
作用:保存程序的当前运行时的堆栈环境至env中,以供longjmp()函数跳转到此处后恢复上下文环境。
void longjmp(jmp_buf env,int value)
作用:发生异常时,待中断处理函数执行完后,跳转到调用setjmp()函数处,通过之前调用setjmp()函数保存到参数env的数据,恢复上下文环境,同时将value值赋给setjmp()函数,而后主程序正常执行。我们就这样说读者可能就得有点抽象了,那我们还是来看看一段代码后再来分析吧!
testing codes:
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
void handler(void)
{
longjmp(buf,1);
}
int main()
{
double a,b;
printf("请输入被除数:");
scanf("%lf",&a);
printf("请输入除数:");
if(setjmp(buf)==0) {
scanf("%lf",&b);
if(0==b) handler();
printf("相除的结果为:%f ",a/b);
} else {
printf("出现错误除数为0 ");
exit(1);
}
return 0;
}
运行结果为:
请输入被除数:12
请输入除数:0
出现错误除数为0
Press any key to continue
程序分析:
在main()函数中,我们最初调用setjmp()函数,保存当前的上下文环境至buf中,同时函数返回0;然后逐步往下运行,运行到我们输入0时,通过if语句发现b的值为0,那么就调用handler()函数来进行处理,在该函数中我们使用了longjmp()函数,我们知道第一个参数的作用是用来得到最初调用setjmp()函数是的环境信息,以便在使用longjmp()函数的时候能够正确的返回到setjmp()函数最初的调用处,而后面的参数表示的返回到setjmp()函数的时候的返回值。我们在此返回1,所以执行else部分的语句。
值得注意的地方:
1.在setjmp与longjmp结合使用时,它们必须有严格的先后执行顺序,先调用setjmp函数,之后再调用longjmp函数,以恢复到先前被保存的“程序执行点”。否则,假如在setjmp调用之前,执行longjmp函数,将导致程序的执行流变的不可猜测,很轻易导致程序崩溃而退出。
2.源代码中要包含头文件<setjmp.h>
利用signal()函数实现异常处理
利用signal()函数,系统自动的捕捉到程序执行过程中产生的异常信号,并根据信号类型调用相对应的异常处理函数,处理完之后自动返回到异常发生指令以后,接着执行。接下来我们来看看一段使用signal捕捉除数为0时候的异常代码。
testing codes:
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <float.h>
#include <string.h>
jmp_buf buf;
int err;
void handler( int num )
{
err = num;
printf( "发生浮点计算异常 ");
longjmp( buf, 1);
}
int main( void )
{
double a, b;
char str[20];
int ret;
_control87( 0, _MCW_EM );
if( signal( SIGFPE, handler ) == SIG_ERR ) { printf("绑定失败 " );
abort();
}
ret = setjmp( buf );
if(0 == ret ) {
printf("请输入被除数:");
scanf("%lf",&a);
printf("请输入除数:");
scanf("%lf",&b);
printf( "a / b = %4.3g ", a/b);
printf("发生异常时候不会被执行的语句 ");
}
return 0;
}
没有发生异常时候的运行结果:
请输入被除数:123
请输入除数:3
a / b = 41
发生异常时候不会被执行的语句
Press any key to continue
发生异常时候的运行结果:
请输入被除数:12
请输入除数:0
发生浮点计算异常
Press any key to continue
程序分析:
先看看_control87( 0, _MCW_EM );这句,它的功能是开启所有的浮点计算异常,通常情况下浮点计算异常是被屏蔽掉的,我们为了能够使得接下来的signal能够捕捉到浮点计算异常,所以要将其开启。
在往下看我们通过signal( SIGFPE, handler )来绑定了一个浮点计算异常处理函数,如果发生异常时,那么就调用handler()函数来处理。接下来通过ret = setjmp( buf );保存程序运行的环境信息,以便接下来的调用longjmp()函数能够根据这个保存的信息返回该程序先前setjmp()函数的执行点。同时我们对比两次运行的结果发现如果发现异常的时候接下来的打印语句“printf("发生异常时候不会被执行的语句 ");”是不会被执行的,直接跳转到我们绑定的handler()函数执行了。后面我想就不需要我来解释了。
我们在此仅仅是举一些简单的代码教会读者学会使用setjmp()函数和longjmp()函数来实现异常处理,你完全可以在此基础上编写出复杂的异常处理。