我尝试用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给出相同的输出。
所以,我的问题是,为什么这个小程序在优化下表现得如此不同?
答案 0 :(得分:1)
实际上,您的程序会显示未定义的行为,这就是您所看到的。
上下文函数不是标准C库的一部分,它们不再是Posix标准的一部分,但这不是您的程序具有未定义行为的原因。问题是getcontext
和setcontext
必须使用与setjmp
和longjmp
相同的限制(它们仍然是标准C的一部分)。这些限制在C标准中描述(应在联机帮助页中进行描述)。特别是,请阅读§7.13.2.1 paragraph 3。在longjmp
之后(与setcontext
一样):
...自动存储持续时间的对象的值是包含相应setjmp宏的调用的函数的本地值,该函数没有volatile限定类型并且在setjmp调用和longjmp调用之间已经更改,这是不确定的。
特别是,参数k
和l
属于此类别(调用函数getcontext
/的自动存储持续时间/本地对象)已更改。所以它们的值是不确定的,并且不需要优化来保存它们。他们没有。
注意:由于上下文函数不再出现在Posix中,因此很难证实上述情况。较旧的Posix版本似乎没有提到需要将局部变量声明为volatile
,如果它们可能会改变,尽管Gnu libc手册中有一个副手注释。也许这只是民间传说编程的一部分。但它确实如此:优化器不知道上下文函数,也不需要知道setjmp
,因此它不知道getcontext()
之后的代码可能被执行多次。因此,它没有理由确保局部变量真正被修改,除非它们是volatile
,在这种情况下它没有选择。