1. 非局部跳转函数 - setjmp 和 longjmp 函数
非局部指的是,这不是由普通C语言goto语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,
返回到当前函数调用路径上的某一个函数中。(简单点基本这样说:goto 语句用于同一个函数内跳转,
而 setjmp 和 longjmp的组合,不但可以实现函数内跳转,还可以实现函数间跳转)
特别注意,setjmp函数与longjmp函数必须要组合起来使用,它们是紧密相关的一对操作,只有将它们结合起来使
用,才能达到程序跳转控制的目的。它们在处理出现在深层函数嵌套的错误情况时很有用处,
也可以使用它们对程序中可能出现的异常进行集中处理。
2.函数原型:
#include <setjmp.h>
int setjmp(jmp_buf env); 返回值: 若直接调用则返回0, 若从longjmp调用返回则返回非 0 值。
void longjmp(jmp_buf env, int val); |
函数说明:
1)setjmp 的参数env 类型是一个特殊类型jmp_buf。这一数据类型是某种形式的数组,其中存放在调用longjmp时能
用来恢复栈状态的所有信息。因为需要在另一个函数中引用env变量,所以规范的处理方式是将env 变量定义为全局变量。
2)对于同一个 setjmp 可以有多个longjmp。例如,可以在func1 中以val 为 1 调用longjmp,
也可以在func2 中以val 为 2 调用longjmp。如果在 func1中返回,则setjmp的返回值就是1, 如果在func2 中返回,
则setjmp的返回值就是2,通过测试返回值就可以判断造成返回的longjmp 是在func1 还是 func2 中。
3)setjmp可以返回两个不同的值。当第一次直接调用setjmp时,返回值为0。
当从longjmp函数返回时,setjmp函数的返回值为longjmp的第二个参数的值。
4)longjmp 函数的第一个参数就是在调用setjmp 时所用的env,第二个参数是具有非0值的val。
调用longjmp函数时不能使setjmp函数返回0,如果val为0,则setjmp函数返回1。longjmp函数从来不返回,因为它
调用后就跳转到setjmp函数保存的堆栈处,恢复堆栈开始执行,所以longjmp函数不会返回。
3.最简单的一个例子:
#include <stdio.h> #include <stdlib.h> #include <setjmp.h>
jmp_buf jmpbuffer;
int main(int argc,char *argv[]) { int ret;
ret = setjmp(jmpbuffer); printf("ret = %d\n", ret); if (ret !=0 ) exit(0);
longjmp(jmpbuffer, 5);
exit(0); } |
4. Automatic, Register, and Volatile Variables
We've seen what the stack looks like after calling longjmp. The next question is,
"what are the states of the automatic variables and register variables in the main function?"
When main is returned to by the longjmp, do these variables have values corresponding to
when the setjmp was previously called (i.e., are their values rolled back),
or are their values left alone so that their values are whatever they were
when do_line was called (which caused cmd_add to be called, which caused longjmp to be called)?
Unfortunately, the answer is "it depends." Most implementations do not try to roll back
these automatic variables and register variables, but the standards say only that
their values are indeterminate. If you have an automatic variable that you don't want rolled back,
define it with the volatile attribute. Variables that are declared global or static are left alone
when longjmp is executed.
注意,全局变量,静态变量,和易失变量(Volatile Variable)不受优化的影响,在调用 longjmp 之后,
他们的值是最近呈现的值。
示例代码:testjmp.c (APUE2 7.10节中的示例代码)
#include <stdio.h> #include <stdlib.h> #include <setjmp.h>
static void f1(int, int, int, int); //static void f2(void);
static jmp_buf jmpbuffer; static int globval;
int main(void) { int autoval; register int regival; volatile int volaval; static int statval;
globval = 1; autoval = 2; regival = 3; volaval = 4; statval = 5;
if (setjmp(jmpbuffer) != 0) { printf("after longjmp:\n"); printf("globval = %d, autoval = %d, regival = %d," " volaval = %d, statval = %d\n", globval, autoval, regival, volaval, statval); exit(0); }
/* * Change variables after setjmp, but before longjmp. */ globval = 95; autoval = 96; regival = 97; volaval = 98; statval = 99;
f1(autoval, regival, volaval, statval); /* never returns */ exit(0); }
static void f1(int i, int j, int k, int l) { printf("in f1():\n"); printf("globval = %d, autoval = %d, regival = %d," " volaval = %d, statval = %d\n", globval, i, j, k, l); //f2();
longjmp(jmpbuffer, 1); }
static void f2(void) { longjmp(jmpbuffer, 1); } |
如果以不带优化和带优化选项对此程序分别进行编译,然后运行它们,则得到不同的结果:
$ cc testjmp.c compile without any optimization
$ ./a.out
in f1():
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
$ cc -O testjmp.c compile with full optimization
$ ./a.out
in f1():
globval = 95, autoval = 96, regival = 97, volaval = 98, statval = 99
after longjmp:
globval = 95, autoval = 2, regival = 3, volaval = 98, statval = 99
阅读(2592) | 评论(0) | 转发(0) |