传统C语言异常处理机制
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,下面列出他们:
int main()
{
jmp_buf main_stat;
if(setjmp(error_stat)!=0)
{
printf(" error in main \n");
return -1;
}
file1();
return 0;
}
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();
}
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);
}
printf(" start error \n");
longjmp(error_stat,1);
}
对他们进行编译,运行,输出结果为:
start error
error in file2
error in file1
error in main
这只是一个很简单的例子,而且是按照层次传递的,不按照层次传递的代码与之类似。
小结
通过使用setjmp/longjmp,可以在C语言中实现模仿C++层次传递的异常处理机制。这种实现方式效率比较高,在大规模工程项目中能够很好的实现异常处理,减轻程序的复杂度。