C语言中要实现"非本地跳转",C标准函数库提供了2个函数setjmp和longjmp来实现这个功能。头文件在<setjmp.h>。setjmp/longjmp的典型用途是例外处理机制的实现:利用longjmp恢复程序或线程的状态,甚至可以跳过栈中多层的函数调用。
函数声明:
SYNOPSIS
#include <setjmp.h>
int setjmp(jmp_buf env);
DESCRIPTION
A call to setjmp() shall save the calling environment in its env argument for later use by longjmp().
作用:建立本地的
jmp_buf
缓冲区并且初始化,用于将来跳转回此处。这个子程序
[1] 保存程序的调用环境于
env
参数所指的缓冲区,
env
将被
longjmp
使用。如果是从
setjmp
直接调用返回,
setjmp
返回值为0。如果是从
longjmp
恢复的程序调用环境返回,
setjmp
返回非零值。
SYNOPSIS
#include <setjmp.h>
void longjmp(jmp_buf env, int val);
DESCRIPTION
The longjmp() function shall restore the environment saved by the most recent invocation of setjmp() in the same thread, with the
corresponding jmp_buf argument. If there is no such invocation, or if the function containing the invocation of setjmp() has ter-
minated execution in the interim, or if the invocation of setjmp() was within the scope of an identifier with variably modified
type and execution has left that scope in the interim, the behavior is undefined. It is unspecified whether longjmp() restores
the signal mask, leaves the signal mask unchanged, or restores it to its value at the time setjmp() was called.
作用:恢复
env
所指的缓冲区中的程序调用环境上下文,
env
所指缓冲区的内容是由
setjmp
子程序
[1]调用所保存。
value
的值从
longjmp
传递给
setjmp
。
longjmp
完成后,程序从对应的
setjmp
调用处继续执行,如同
setjmp
调用刚刚完成。如果
value
传递给
longjmp
零值,
setjmp
的返回值为1;否则,
setjmp
的返回值为
value
。
示例1:
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
static jmp_buf buf;
int
main(void)
{
volatile int b;
b = 3;
if (setjmp(buf) != 0) //第一次调用返回0
{
printf("%d\n", b);
exit(0);
}
b = 5;
longjmp(buf, 1); //val为1时返回setjmp的跳转
return 0;
}
运行结果:
5
示例2:
#include <setjmp.h>
#include "apue.h"
static void f1(int, int, int);
static void f2(void);
static jmp_buf jmpbuffer;
int
main(void)
{
int count;
register int val;
volatile int sum;
count = 2; val = 3; sum = 4;
if (setjmp(jmpbuffer) != 0)
{
printf("after longjmp: count = %d, val = %d, sum = %d\n",
count, val, sum);
exit(0);
}
count = 97; val = 98; sum = 99;
f1(count, val, sum);
return 0;
}
static void f1(int i, int j, int k)
{
printf("in f1(): count = %d, val = %d, sum = %d\n",
i, j, k);
f2();
}
static void f2(void)
{
longjmp(jmpbuffer, 1);
}
如果不使用优化选项编译时
[root@localhost src]# gcc -Wall -I../include fig7_5.c error.c -o fig7_5
[root@localhost src]# ./fig7_5
in f1(): count = 97, val = 98, sum = 99
after longjmp: count = 97, val = 98, sum = 99
如果使用了优化选项后
[root@localhost src]# gcc -Wall -O -I../include fig7_5.c error.c -o fig7_5
[root@localhost src]# ./fig7_5
in f1(): count = 97, val = 98, sum = 99
after longjmp: count = 2, val = 3, sum = 99
从以上可以看出,易失变量(sum )不受优化的影响,在longjmp之后的值,是它在调用f1时的值。在我们所使用的setjmp(3)手册页上说明存放在存储器中的变量将具有longjmp时的值,而在CPU和浮点寄存器中的变量则恢复为调用setjmp时的值。这确实就是在运行程序7-5时所观察到的值。不进行优化时,所有这三个变量都存放在存储器中(亦即对val的寄存器存储类被忽略)。而进行优化时,count和val都存放在寄存器中(即使count并末说明为register) ,volatile变量则仍存放在存储器中。通过这一例子要理解的是,如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。