setjmp和longjmp的"非本地跳转"

来源:互联网 发布:大数据为什么在贵阳 编辑:程序博客网 时间:2024/04/30 22:47

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传递给setjmplongjmp完成后,程序从对应的setjmp调用处继续执行,如同setjmp调用刚刚完成。如果value传递给longjmp零值,setjmp的返回值为1;否则,setjmp的返回值为value


示例1:

#include <stdio.h>#include <stdlib.h>#include <setjmp.h>static jmp_buf buf;intmain(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;intmain(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 = 99after 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_5in f1(): count = 97, val = 98, sum = 99after longjmp: count = 2, val = 3, sum = 99
从以上可以看出,易失变量(sum )不受优化的影响,在longjmp之后的值,是它在调用f1时的值。在我们所使用的setjmp(3)手册页上说明存放在存储器中的变量将具有longjmp时的值,而在CPU和浮点寄存器中的变量则恢复为调用setjmp时的值。这确实就是在运行程序7-5时所观察到的值。不进行优化时,所有这三个变量都存放在存储器中(亦即对val的寄存器存储类被忽略)。而进行优化时,count和val都存放在寄存器中(即使count并末说明为register) ,volatile变量则仍存放在存储器中。通过这一例子要理解的是,如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。

原创粉丝点击