C语言异常捕获机制 - setjmp
快速入门
想快速入门该模块请访问:介绍,数据接口,示例代码
介绍
C语言没有C++或Java的异常捕获机制,但可以通过setjmp/longjmp实现类似的效果:
- 使用setjmp保存当前执行环境到jmp_buf,然后默认返回0。
- 程序继续执行,到某个地方调用longjmp,传入上面保存的jmp_buf,以及另一个整形值(我们称他为异常码)。
- 此时执行点又回到调用setjmp的返回处,且返回值变成longjmp设置的值
- 可以实现跨函数跳转
注意:
-
使用gcc或者clang优化编译的时候,可能导致结果不符合预期。可以参考某大神的示例代码。因此不推荐时候,异常捕获最好还是直接使用返回值形式进行调用。https://gist.github.com/nnkken/8aea1eaf1e43a963a9309c98ae4f891e
-
此外,setjmp只能设置最近一次执行环境,而且longjmp也只能是回到上一次设置setjmp中,因此并不支持设置多个执行环境
数据与接口
头文件
-
#include <setjmp.h>
数据
- 环境堆栈类型 :
jmp_buf
接口
-
设置环境堆栈
int setjmp( jmp_buf env );
-
跳转至环境堆栈并返回指定值
void longjmp( jmp_buf env, int value );
接口介绍
setjmp
setjmp函数用于保存程序的运行时的堆栈环境,接下来的其它地方,你可以通过调用longjmp函数来恢复先前被保存的程序堆栈环境。当 setjmp和longjmp组合一起使用时,它们能提供一种在程序中实现“非本地局部跳转”(“non-local goto”)的机制。并且这种机制常常被用于来实现,把程序的控制流传递到错误处理模块之中;或者程序中不采用正常的返回(return)语句,或函数的正常调用等方法,而使程序能被恢复到先前的一个调用例程(也即函数)中。
对setjmp函数的调用时,会保存程序当前的堆栈环境到env参数中;接下来调用longjmp时,会根据这个曾经保存的变量来恢复先前的环境,并且当前的程序控制流,会因此而返回到先前调用setjmp时的程序执行点。此时,在接下来的控制流的例程中,所能访问的所有的变量(除寄存器类型的变量以外),包含了longjmp函数调用时,所拥有的变量。
longjmp
longjmp函数用于恢复先前程序中调用的setjmp函数时所保存的堆栈环境。setjmp和longjmp组合一起使用时,它们能提供一种在程序中实现“非本地局部跳转”(“non-local goto”)的机制。并且这种机制常常被用于来实现,把程序的控制流传递到错误处理模块,或者不采用正常的返回(return)语句,或函数的正常调用等方法,使程序能被恢复到先前的一个调用例程(也即函数)中。
对setjmp函数的调用时,会保存程序当前的堆栈环境到env参数中;接下来调用longjmp时,会根据这个曾经保存的变量来恢复先前的环境,并且因此当前的程序控制流,会返回到先前调用setjmp时的执行点。此时,value参数值会被setjmp函数所返回,程序继续得以执行。并且,在接下来的控制流的例程中,它所能够访问到的所有的变量(除寄存器类型的变量以外),包含了longjmp函数调用时,所拥有的变量;而寄存器类型的变量将不可预料。setjmp函数返回的值必须是非零值,如果longjmp传送的value参数值为0,那么实际上被setjmp返回的值是1。
伪代码
res = setjmp(env);
if (res == 0) {try dosomething...(contain longjmp)
} else {exception dosomething...
}
示例:比大小
#include <stdio.h>
#include <setjmp.h>#define CORRECT_NUM 5
jmp_buf g_env;void err_1(void)
{printf("raise err 1: num is small\n");longjmp(g_env, 1);
}void err_2(void)
{printf("raise err 2: num is large\n");longjmp(g_env, 2);
}void work_func(int num)
{if (num == CORRECT_NUM) {printf("num is correct\n");} else if (num < CORRECT_NUM) {err_1();} else {err_2();}
}int main() {int num;scanf("%d", &num);int res = setjmp(g_env);if (res == 0) {/* try */work_func(num);} else {/* exception */printf("res = %d\n", res);}return 0;
}
参考链接
- https://zhuanlan.zhihu.com/p/82492121
- http://www.360doc.com/content/22/0119/23/78473664_1014100527.shtml
- https://www.runoob.com/cprogramming/c-standard-library-setjmp-h.html
- https://gist.github.com/nnkken/8aea1eaf1e43a963a9309c98ae4f891e