C语言利用setjmp/longjmp实现模仿C++的层次异常处理机制
传统C语言异常处理机制
传统的C语言异常处理,一般情况下为被调用者通过设置不同的返回值或者设置一个表示错误的全局变量值,以代表执行正常或者是发生了各种各样的错误,异常,警告等等。而调用者则通过被调用者的返回值来判断是否发生了异常,并对异常进行处理。以下是这种处理方式的简单示例:
[cpp]
fun1()
{
....
int result=fun2();
switch(result)
{
case 1 ....
case 2 ....
....
}
....
}
fun2()
{
....
.... return -1;
....
.... return 1;
....
}
更加合理的异常处理机制
在传统的C语言异常处理机制中,若需要按照层次传递异常,则每一层都要判断每一个下层接口的返回值,并编写相应的错误处理代码。本层无法处理的错误还要上传至上层接口处理。这样一层一层的调用,当层次较多时,错误会向上逐层传递,需要编写大量代码,给程序员造成不小的麻烦并且很容易出错。此外大量错误处理代码还会降低接口主要流程的可读性。虽然经过不断的检查与调试,发生错误是小概率事件,却需要每层都进行判断,影响效率。
因此,我们需要反思一下,在大规模C工程中,我们到底需要什么样的异常处理机制?例如JAVA,C++中的try/catch/throw异常处理机制更加有效,当发生错误后能够立即跳转到有能力处理该错误的高层接口,而无需逐层传递该异常。
具体的实现细节
在C语言中,我们可以通过setjmp/longjmp实现与C++异常处理相类似的机制。
他们的原型为:int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
使用setjmp/longjmp必须要在程序中添加setjmp.h这个头文件。setjmp()的作用是在缓冲区envbuf中保存系统堆栈中的内容,该宏的返回值为0。而longjmp()使程序跳转到最近一次调用setjmp()处,然后重新执行下面的语句。但是longjmp()的第二个参数会传递一个值递给setjmp(),这个值就是调用longjmp()后再次执行setjmp()的返回值,这样就利用不同的返回值与第一次调用setjmp()区别开。
需要说明的是setjmp/longjmp可以跨函数,因此可以利用他们实现跨函数调用层次的异常处理机制。
下面是一个很简单的使用例子:
我们有3个小文件分别为main.c file1.c file2.c,下面列出他们:
[cpp]
int main()
{
jmp_buf main_stat;
if(setjmp(error_stat)!=0)
{
printf(" error in main \n");
return -1;
}
file1();
return 0;
}
[cpp]
int file1()
{
jmp_buf file1_stat;
memcpy(&file1_stat,&error_stat,sizeof(file1_stat));
if(setjmp(error_stat)!=0)
{
printf(" error in file1 \n");
memcpy(&error_stat,&file1_stat,sizeof(file1_stat));
longjmp(error_stat,1);
}
file2();
}
[cpp] view plaincopy
int file2()
{
jmp_buf file2_stat;
memcpy(&file2_stat,&error_stat,sizeof(file2_stat));
if(setjmp(error_stat)!=0)
{
printf(" error in file2 \n");
memcpy(&error_stat,&file2_stat,sizeof(file2_stat));
longjmp(error_stat,1);
} www.zzzyk.com
printf(" start error \n");
longjmp(error_stat,1);
}
对他们进行编译,运行,输出结果为:
start error
error in file2
error in file1
error in main
这只是一个很简单的例子,而且是按照层次传递的,不按照层次传递的代码与之类似。
小结
通过使用setjmp/longjmp,可以在C语言中实现模仿C++层次传递的异常处理机制。这种实现方式效率比较高,在大规模工程项目中能够很好的实现异常处理,减轻程序的复杂度。
补充:软件开发 , C语言 ,