C语言中有一个goto语句,其可以结合标号实现函数内部的任意跳转(通常情况下,很多人都建议不要使用goto语句,因为采用goto语句后,代码维护工作量加大)。另外,C语言标准中还提供一种非局部跳转“no-local goto”,通过setjmp和longjmp函数来实现。
下面我们来看一个简单的例子。
1 #include <stdio.h>
2 #include <setjmp.h>
3
4 jmp_buf jump_buffer;
5
6 void func(void)
7 {
8 printf("Before calling longjmp\n");
9 longjmp(jump_buffer, 1);
10 printf("After calling longjmp\n");
11 }
12 void func1(void)
13 {
14 printf("Before calling func\n");
15 func();
16 printf("After calling func\n");
17 }
18 int main()
19 {
20 if (setjmp(jump_buffer) == 0){
21 printf("first calling set_jmp\n");
22 func1();
23 }else {
24 printf("second calling set_jmp\n");
25 }
26 return 0;
27 }
代码的运行结果如下
lienhua34@lienhua34-laptop:~/program/test$ ./test
first calling set_jmp
Before calling func
Before calling longjmp
second calling set_jmp
通过上面这个简单例子的运行结果可以看出。main函数运行的setjmp()宏调用,将当前程序点的系统状态信息保存到全局变量jump_buffer中,然后返回结果0。于是,代码打印出字符串”first calling set_jmp”,然后调用函数func1()。在函数func1中,先打印字符串”Before calling func”,然后去调用函数func()。现在程序控制流转到func函数中,函数func先打印字符串“Before calling longjmp”,然后调用函数longjmp。这时候关键点到了!!!longjmp函数将main函数中setjmp()宏调用设置在全局变量jump_buffer中的系统状态信息恢复到系统的相应寄存器中,导致程序的控制流跳转到了main函数中setjmp()宏调用所在的程序点,此时相当于第二次进行setjmp()宏调用,并且此时的setjmp()宏调用的返回不再是0,而是传递给函数调用longjmp()的第二个参数1。于是程序控制流转到main函数中if语句的else部分执行,打印字符串“second calling set_jmp“。最后,执行main函数中的语句“reture 0;”返回,程序运行结束退出。
从上面的运行过程,我们可以看出在longjmp()函数调用处的程序点嵌套在三层函数调用中:main, func1和func,但是longjmp()函数调用导致程序控制流跳过函数调用func和func1,直接回到main函数中setjmp()宏调用所在的程序点,然后执行main函数中后续的语句,从而忽略了函数func1和func中后续的语句部分。这就是非局部跳转。