在C语言中,当嵌套函数调用出错时,可以采用非局部跳转函数setjmp和longjmp,利用这两个函数可以把程序控制流进行有效转移,这两个函数相结合可以在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。
/* setjmp 和 longjmp 函数 */ /* * 函数功能:当某个嵌套函数出错时,可实现程序控制流跳转; * 函数原型: * #include <setjmp.h> * int setjmp(jmp_buf env);//若直接调用则返回0,若从longjmp调用则返回非0值; * * void longjmp(jmp_buf env, int val); * 说明: * 参数env是某种形式的数组,存放在调用longjmp时能恢复栈状态的所有信息,一般定义为全局变量; * val设置setjmp的返回值; */
测试程序
#include <setjmp.h> #include "apue.h" static jmp_buf env; static int globval;//全局变量 static void fun1(int,int,int,int); static void fun2(void); int main(void) { int autoval;//自动变量 register int registerval;//寄存器变量 volatile int volatileval;//易失变量 static int staticval;//静态变量 globval = 1; autoval = 2; registerval = 3; volatileval = 4; staticval = 5; if(setjmp(env) != 0) { printf("after longjmp:\n"); printf("globval= %d, autoval= %d, registerval= %d," "volatileval= %d, staticval= %d\n", globval,autoval,registerval,volatileval,staticval); printf("longjmp error\n"); exit(0); } globval = 10; autoval = 11; registerval = 12; volatileval = 13; staticval = 14; fun1(autoval,registerval,volatileval,staticval); exit(0); } static void fun1(int i, int j, int k, int l) { printf("in funi():\n"); printf("globval= %d, autoval= %d, registerval= %d," "volatileval= %d, staticval= %d\n", globval,i,j,k,l); fun2(); } static void fun2(void) { longjmp(env,1); }输出结果:
没有优化编译的结果:
in funi(): globval= 10, autoval= 11, registerval= 12,volatileval= 13, staticval= 14 after longjmp: globval= 10, autoval= 11, registerval= 3,volatileval= 13, staticval= 14 longjmp error优化编译的结果:
in funi(): globval= 10, autoval= 11, registerval= 12,volatileval= 13, staticval= 14 after longjmp: globval= 10, autoval= 2, registerval= 3,volatileval= 13, staticval= 14 longjmp error
测序讲解:
1、在main函数中,第一次直接调用setjmp时返回值是0,所以不执行main函数的打印部分,直接调用fun1函数;
2、在fun1函数中,首先打印两条语句,然后调用fun2函数。
printf("in funi():\n"); printf("globval= %d, autoval= %d, registerval= %d," "volatileval= %d, staticval= %d\n", i,j,k,l);
3、在fun2函数中,调用longjmp函数,相当于在这里程序出错,返回到main函数的setjmp处,这次是main函数中的setjmp返回值是val=1,则执行以下的打印信息,然后exit退出main函数:
printf("after longjmp:\n"); printf("globval= %d, autoval= %d, registerval= %d," "volatileval= %d, staticval= %d\n", globval,autoval,registerval,volatileval,staticval); printf("longjmp error\n");
关于全局变量、自动变量、寄存器变量、易失变量、静态变量在setjmp和longjmp函数中的变化是有区别的,存放在存储器的变量将和longjmp时的值一样,存放在CPU和浮点寄存器的变量则会恢复为调用setjmp时的值。自动变量、寄存器变量存放在寄存器中,所以其变量值恢复到调用setjmp时的值,全局变量、易失变量、静态变量存放在存储器中,所以变量将和longjmp时的值一样。
参考资料:
《unix高级环境编程》