分类:
2008-11-18 15:45:15
7.10 setjmp and longjmp functions
C语言里的setjmp和longjmp可以实现跨越函数体的跳跃,不过要求在跳跃之前,setjmp要被执行到,而且setjmp执行点位于当前stack frame里的较前的,或者顶多与当前调用longjum函数处于一个stack frame里,即在函数调用路径上。否则没办法跳过去。
还有,当跳过去后,当前的一些寄存器,各个变量的状态是什么?有如下规律:
如果你所使用的变量是在memory里,那么它的状态就是在发生跳跃时的状态。如果你的变量在register里,而不是在memory里,那么它的状态就是在调用setjmp时的状态,即有roll back到了以前。那么何种变量在内存,何种在register? 这个取决于你是否制定register, static, volatile, 以及是否是全局变量,以及是否使用了编译器的优化。
1. 全局变量,static 局部变量,都在内存中
2. Volatile的局部变量肯定在内存中
3. 没有任何修饰的局部变量就是自动变量,在内存中
4. Register变量在打开compiler优化时在register里,否则还是在内存中。
结论:要想让变量状态roll back就将其设为register变量,并且要打开编译器的优化。但是,我觉得一般来说,可能大都不希望变量状态被rollback。所以我们建议不使用register变量。
为了安全,我们建议,将变量都使用volatile修饰符。
|
#include int
setjmp(jmp_buf env); |
|
Returns: 0 if called directly, nonzero if returning from a call to
longjmp |
|
void
longjmp(jmp_buf env, int val); |
下面是一个使用setjmp和longjmp并且展现不同类型修饰符的变量的状态变化的例子,并且对是否打开了编译器优化进行了对比:
#include "apue.h"#include static void f1(int, int, int, int);static void f2(void); static jmp_buf jmpbuffer;static int globval; intmain(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 voidf1(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();}static voidf2(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
网上有人用setjmp和longjmp在C语言里实现了类似C++里的try, catch, throw的异常处理机制,并且将SIGNAL榜定到一个自己的函数上,并且也throw,这样就给C语言增加了异常处理机制,呵呵。主要是是用宏实现的,自己定义了一个exception结构,全局的链表来维护所有的exception。堆栈式操作这个链表。
这个内容我会放到C/C++部分的博客里。连接:
http://blog.chinaunix.net/u/5958/showart_38725.html
在C++里不应该使用setjmp和longjump,因为他们不遵守C++的对象模型。一些stack frame里的对象不被释放。具体一点,网上有人做了测试:得出一个规律:调用longjmp的那个函数的stack frame里的变量不会被析构,而其stack frame以前的函数调用的stack frame里的局部变量就会被析构。所以并不是完全不遵从C++语义。他还做了个测试。
下面摘在该人做的测试的连接,也放在我的C/C++的博客内容里:
连接:
MSDN里提到,如果非要用setjmp和longjmp在C++程序里,那么要include的头文件是:SETJMP.h和SETJMPEX.h
suanmeilizhi2011-12-17 14:51:35
看到APUE第七章,longjmp的例子,书上说,进行优化之后,自动变量和寄存器变量都存放在寄存器中,但是我用objdump发现,优化之后的自动变量和寄存器变量直接被立即数代替了,并不是书上说的情况,这个现象怎么解释?