getcontext和savecontext在优化下表现不同

时间:2017-05-09 20:37:59

标签: c

我尝试用getcontext和setcontext实现fibonacci。在没有优化的情况下编译时,工作正常:

#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
static int result = 0;
static ucontext_t ctx; 


// fibonacci through get and set context 
int fib(int k, int l, int m ) {
    static int time = 0;
    getcontext(&ctx);

    time++;
    printf("In fib, time: %d\n", time); 
    int p = k; 
    k = l;
    l = p + l;
    printf("(k,l): (%d, %d) > %d\n", k, l, m);

    if(k > m){
            printf("k > m: %d > %d\n", k, m);
            return result;
    } 

    setcontext((const ucontext_t *)&ctx);


    return 0; // will never happen


 }

int main(int argc, char **argv){

    int f = fib(1,1,100);
    printf("fib(1,1,100): %d\n", f);


}

然而,当使用优化(在gcc和clang上测试)执行此操作时,它开始永远循环。

之前的输出:

 
In fib, time: 1
(k,l): (1, 2) > 100
In fib, time: 2
(k,l): (2, 3) > 100
In fib, time: 3
(k,l): (3, 5) > 100
In fib, time: 4
(k,l): (5, 8) > 100
In fib, time: 5
(k,l): (8, 13) > 100
In fib, time: 6
(k,l): (13, 21) > 100
In fib, time: 7
(k,l): (21, 34) > 100
In fib, time: 8
(k,l): (34, 55) > 100
In fib, time: 9
(k,l): (55, 89) > 100
In fib, time: 10
(k,l): (89, 144) > 100
In fib, time: 11
(k,l): (144, 233) > 100
k > m: 144 > 100
fib(1,1,100): 0

优化后的输出:

In fib, time: 1
(k,l): (1, 2) > 100
In fib, time: 2
(k,l): (1, 2) > 100
In fib, time: 3
(k,l): (1, 2) > 100
In fib, time: 4
(k,l): (1, 2) > 100
In fib, time: 5
(k,l): (1, 2) > 100

似乎在优化下,k和l保持不变,而时间更新。我知道我不应该使用这些功能,因为它们已被弃用而且我不会。我只是在玩弄。

我试图理解手册中的行为,但不知怎的,我无法解释这种行为。我认为有一个明确的原因,因为clang和gcc给出相同的输出。

所以,我的问题是,为什么这个小程序在优化下表现得如此不同?

1 个答案:

答案 0 :(得分:1)

实际上,您的程序会显示未定义的行为,这就是您所看到的。

上下文函数不是标准C库的一部分,它们不再是Posix标准的一部分,但这不是您的程序具有未定义行为的原因。问题是getcontextsetcontext必须使用与setjmplongjmp相同的限制(它们仍然是标准C的一部分)。这些限制在C标准中描述(应在联机帮助页中进行描述)。特别是,请阅读§7.13.2.1 paragraph 3。在longjmp之后(与setcontext一样):

  

...自动存储持续时间的对象的值是包含相应setjmp宏的调用的函数的本地值,该函数没有volatile限定类型并且在setjmp调用和longjmp调用之间已经更改,这是不确定的。

特别是,参数kl属于此类别(调用函数getcontext /的自动存储持续时间/本地对象)已更改。所以它们的值是不确定的,并且不需要优化来保存它们。他们没有。

注意:由于上下文函数不再出现在Posix中,因此很难证实上述情况。较旧的Posix版本似乎没有提到需要将局部变量声明为volatile,如果它们可能会改变,尽管Gnu libc手册中有一个副手注释。也许这只是民间传说编程的一部分。但它确实如此:优化器不知道上下文函数,也不需要知道setjmp,因此它不知道getcontext()之后的代码可能被执行多次。因此,它没有理由确保局部变量真正被修改,除非它们是volatile,在这种情况下它没有选择。