Chinaunix首页 | 论坛 | 博客
  • 博客访问: 968728
  • 博文数量: 200
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 2479
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 15:07
文章分类

全部博文(200)

文章存档

2009年(12)

2008年(190)

我的朋友

分类:

2008-11-18 15:45:15

7.10 setjmp and longjmp functions

C语言里的setjmplongjmp可以实现跨越函数体的跳跃,不过要求在跳跃之前,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);

 

 

下面是一个使用setjmplongjmp并且展现不同类型修饰符的变量的状态变化的例子,并且对是否打开了编译器优化进行了对比:

Figure 7.13. Effect of longjmp on various types of variables
#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

 

网上有人用setjmplongjmpC语言里实现了类似C++里的try, catch, throw的异常处理机制,并且将SIGNAL榜定到一个自己的函数上,并且也throw,这样就给C语言增加了异常处理机制,呵呵。主要是是用宏实现的,自己定义了一个exception结构,全局的链表来维护所有的exception。堆栈式操作这个链表。

这个内容我会放到C/C++部分的博客里。连接:

http://blog.chinaunix.net/u/5958/showart_38725.html

 

C++里不应该使用setjmplongjump,因为他们不遵守C++的对象模型。一些stack frame里的对象不被释放。具体一点,网上有人做了测试:得出一个规律:调用longjmp的那个函数的stack frame里的变量不会被析构,而其stack frame以前的函数调用的stack frame里的局部变量就会被析构。所以并不是完全不遵从C++语义。他还做了个测试。

下面摘在该人做的测试的连接,也放在我的C/C++的博客内容里:

连接:

 

MSDN里提到,如果非要用setjmplongjmpC++程序里,那么要include的头文件是:SETJMP.hSETJMPEX.h

阅读(454) | 评论(1) | 转发(0) |
0

上一篇:7.8 memory allocation

下一篇:7 excercises

给主人留下些什么吧!~~

suanmeilizhi2011-12-17 14:51:35

看到APUE第七章,longjmp的例子,书上说,进行优化之后,自动变量和寄存器变量都存放在寄存器中,但是我用objdump发现,优化之后的自动变量和寄存器变量直接被立即数代替了,并不是书上说的情况,这个现象怎么解释?