在C中有时我们会使用goto语句用于运行跳转,可是不能跨越函数
#include <stdio.h> void func2() { int num = 0; dst2: if (num > 0) { printf("func1() "); func3(); } if (num == 1) return; num++; goto dst2; } void func3() { } void func1() { dst1: func2(); goto dst2;//error } int main() { func1(); return 0; }
而在深层嵌套函数中的跳转,能够使用setjmp和longjmp函数。
这两个函数对于处理发生在深层嵌套函数中出错的情况是很实用的。
函数原型:
#include <setjmp.h> int setjmp(jmp_buf env); 返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值 void longjmp(jmp_buf env, int val);參数说明:
setjmp的env參数的类型是一个特殊类型jmp_buf,这一数据类型是某种形式的数组,当中存放存放在调用longjmp时能用来恢复栈状态的全部信息
longjmp的env參数指定恢复栈到某个状态,val为非0值,使用第二个參数的原因是对于一个setjmp能够有多个longjmp。比如。在func2()中以val为2调用longjmp,
func3()以val3调用longjmp,调用longjmp导致程序跳转到对应的指定env处的setjmp.并导致setjmp返回val值。通过測试返回值可推断造成返回的longjmp是在func2()中还是func3()中
实例:
#include <stdio.h> #include <setjmp.h> jmp_buf jmpbuffer; int times = 0; void func3() { times++; longjmp(jmpbuffer, 3); } void func2() { func3(); } void func1() { int r; r = setjmp(jmpbuffer); printf("%d ", r); if (times == 1) return; func2(); } int main() { func1(); return 0; }
longjmp跳转对变量的影响
longjmp实际上恢复栈的状态。所以我们能够猜測,对于全局变量和静态变量。不存储在栈区中,所以值不会受影响,而大多数实现不会回滚自己主动变量的值
若不希望自己主动变量的值被回滚,能够指定其具有volatile属性。
很多其它关于volatile见 http://blog.csdn.net/aspnet_lyc/article/details/17231989
实例:
#include <stdio.h> #include <setjmp.h> #include <stdlib.h> jmp_buf jmpbuffer; void func1() { int auto_data = 0; static int static_data = 0; volatile int volatile_data = 0; if (setjmp(jmpbuffer) != 0) { printf("after jmp: "); printf("auto: %d ", auto_data); printf("static: %d ", static_data); printf("volatile: %d ", volatile_data); exit(0); } printf("auto: %d ", auto_data); printf("static: %d ", static_data); printf("volatile: %d ", volatile_data); auto_data++; static_data++; volatile_data++; longjmp(jmpbuffer, 3); } int main() { func1(); return 0; }
以不带优化和带优化选项对此程序部分进行编译,运行结果:
不带优化编译gcc
带优化编译 gcc -O
存放在存储器中的变量将具有longjmp时的值,而在cpu寄存器中的变量则恢复为调用setjmp时的值。
不进行优化时,自己主动变量存放在存储器中。
优化后,自己主动变量存放寄存器中,这就是优化编译后auto_data值变为0的原因。