分类:
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;
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();
}
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
网上有人用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发现,优化之后的自动变量和寄存器变量直接被立即数代替了,并不是书上说的情况,这个现象怎么解释?