首先介绍下与非本地跳转的对应的本地跳转,本地跳转指的是类似于goto语句的一系列应用,当设置了标志之后,可以跳到所在函数内部的标号上。然而,本地跳转不能将控制权转移到所在程序的任意地点,不能跨越函数,因此也就有了非本地跳转。
1.非本地跳转是C语言提供的一种用户级的异常控制流的形式,它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列。
非本地跳转是通过setjmp和longjmp函数提供的。
我们来看看函数:
1 #include <setjmp.h> 2 //setjmp函数在env缓冲区中保存当前调用环境,以供后面的longjmp使用 3 int setjmp(jmp_buf env); 4 int sigsetjmp(sigjmp_buf env,int savesigs); 5 //sigsetjmp函数和siglongjmp函数是setjmp和longjmp的可以被信号处理程序使用的版本 6 //longjmp函数从env缓冲区中恢复调用环境,然后触发一个最近一次初始化env的setjmp调用的返回,然后setjmp返回,并带有非零的返回值retval 7 void longjmp(jmp_buf env,int retval); 8 void siglongjmp(sigjmp_buf env,int retval);
返回值:若直接调用则返回0,若从siglongjmp调用返回则返回非0值
要注意一点,setjmp的返回值不能被赋值给变量。但是可以用在switch语句或者条件语句的测试中
setjmp函数只调用一次,但是返回多次,一次是当第一次调用setjmp,而调用环境保存在缓冲区env中时,一次是为每个相应的longjmp调用。
longjmp函数被调用一次但从不返回。
2.应用
2.1非本地跳转允许从一个深层嵌套的函数调用中立即返回,通常由检测到某个错误引起的。
示例代码:
1 #include "csapp.h" 2 3 jmp_buf buf; 4 5 int error1 = 0; 6 int error2 = 1; 7 8 void foo(void), bar(void); 9 10 int main() 11 { 12 int rc; 13 14 rc = setjmp(buf); 15 if (rc == 0) 16 foo(); 17 else if (rc == 1) 18 printf("Detected an error1 condition in foo "); 19 else if (rc == 2) 20 printf("Detected an error2 condition in foo "); 21 else 22 printf("Unknown error condition in foo "); 23 exit(0); 24 } 25 26 /* deeply nested function foo */ 27 void foo(void) 28 { 29 if (error1) 30 longjmp(buf, 1); 31 bar(); 32 } 33 34 void bar(void) 35 { 36 if (error2) 37 longjmp(buf, 2); 38 }
2.2使信号处理程序分支到一个特殊的代码位置,而不是返回到被信号到达中断了的指令位置
示例代码:
这个代码挺有意思
1 #include "csapp.h" 2 3 sigjmp_buf buf; 4 5 void handler(int sig) 6 { 7 siglongjmp(buf, 1); 8 } 9 10 int main() 11 { 12 Signal(SIGINT, handler); 13 14 if (!sigsetjmp(buf, 1)) 15 /*The initial call to the sigsetjmp function saves the stack and signal context when the program first starts.*/ 16 printf("starting "); 17 else 18 printf("restarting "); 19 20 while(1) { 21 Sleep(1); 22 printf("processing... "); 23 } 24 exit(0); 25 }