库函数setjmp和longjmp可以实现跳转到当前执行函数之外的某个位置,虽然此功能就像goto语句一样使程序不好维护,但是当一个深度嵌套的函数中发生了错误,需要放弃当前任务,从多层函数调用中返回到较高层,此时setjmp和longjmp就派上了用场
#include<setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env,int val);
setjmp()函数的位置为调用longjmp()函数后的跳转目标,可以这样认为,当调用了longjmp()后程序好像执行了回退功能一样,又从setjmp()的位置开始执行了。
setjmp()和longjmp()调用的参数env是一个全局变量,它里面保存了setjmp()当前执行环境的各种信息,之后调用的longjmp()也是这个全局变量env,env中保存了调用setjmp()函数的当前进程的信息,还保存了程序计数寄存器(指向当前正在执行的机器指令)和栈指针寄存器(标记栈顶的)的副本,正是这些信息可以使调用longjmp()时完成俩个关键步骤:
(1)将longjmp()调用的函数与之前调用setjmp()的函数之间的函数栈帧(保存函数实参,局部变量,函数链接信息等)从函数栈中脱离,通过栈指针寄存器重置为env保存的值(此时栈顶就是调用setjmp()函数的栈帧了)
(2)重置程序计数寄存器,使程序可以从初始的setjmp()处开始执行,此功能是通过env参数中保存的值来实现的
#include<stdio.h>
#include<setjmp.h>
static jmp_buf env;
void f2(void)
{
longjmp(env,2);
}
void f1(int argc)
{
if(argc == 1)
{
longjmp(env,1);
}
f2();
}
int main(int argc,char **argv)
{
switch(setjmp(env))
{
case 0:
printf("Calling f1 after the initial setjmp\n");
f1(argc);
break;
case 1:
printf("jumped from f1\n");
break;
case 2:
printf("jump from f2\n");
break;
}
return 0;
}
该程序通过setjmp的初始调用建立了一个跳转目标,通过swich判断setjmp()是初次调用函数二次调用,具体的运行结果由读者自己去测试
setjmp()和longjmp()是一对函数,我们可以使用他们使我们的程序类似回退到我们想回去的地方,setjmp()就是记录我们想要回退的位置的函数,而longjmp()就是触发回退的函数