在C中有时我们会使用goto语句用于执行跳转,但是不能跨越函数
#include <stdio.h>
void func2()
{
int num = 0;
dst2:
if (num > 0)
{
printf("func1()\n");
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\n", 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:\n");
printf("auto: %d\n", auto_data);
printf("static: %d\n", static_data);
printf("volatile: %d\n", volatile_data);
exit(0);
}
printf("auto: %d\n", auto_data);
printf("static: %d\n", static_data);
printf("volatile: %d\n", 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的原因。