这是C代码快照:
int* f(int x) {
static int y;
y = x * x;
return &y;
}
float* g(float x) {
static float y;
y = x * x;
return &y;
}
int main(void) {
printf("*f(1)=%d\n", *f(1));
printf("*f(2)=%d\n", *f(2));
printf("*f(1) + *f(2)=%d\n", *f(1) + *f(2));
printf("*g(1.0)=%f\n", *g(1.0));
printf("*g(2.0)=%f\n", *g(2.0));
printf("*g(1.0) + *g(2.0)=%f\n", *g(1.0) + *g(2.0));
return 0;
}
输出结果为:
*f(1)=1
*f(2)=4
*f(1) + *f(2)=5
*g(1.0)=1.000000
*g(2.0)=4.000000
*g(1.0) + *g(2.0)=8.000000
我并不完全理解f()和g()的双重行为。首先,我怀疑这是编译器问题,但BCC或GCC提供相同的输出。
*f(1) + *f(2)
输出不应等于*g(1.0) + g(2.0)
吗? (5
5.0
或8
8.0
)
答案 0 :(得分:4)
我相信奥利是对的。更明确地说,这取决于在添加发生之前如何存储值。如果在存储值之前执行*g(1.0)
,然后执行*g(2.0)
,则会添加4.0 + 4.0 = 8.0
(请记住,每个指针指向同一静态变量的地址)。否则,如果您执行*g(1.0)
并将其值存储在寄存器中,然后执行*g(2.0)
并添加结果,您将获得1.0 + 4.0 = 5.0
。
所以,实际上,这取决于编译器如何将其写入机器代码。考虑下面的伪x86程序集(为简单起见,我们使用int而不是float):
push 1
call g ; First call to g(1);
add esp, 4 ; Pop value 1
mov ebx, eax ; Save our pointer
push 2
call g ; Call to g(2)
add esp, 4 ; Pop value 2 -- Remember that eax == ebx, now (they point to same address)
mov eax, [eax] ; Forget the pointer, store the value (4).
mov ebx, [ebx] ; Do the same thing, value is 4 since they point to same place
add eax, ebx ; Add our two values. 4 + 4 = 8
ret
相反,请考虑以下
push 1
call g ; First call to g(1);
add esp, 4 ; Pop value 1
mov ebx, [eax] ; Save the value at the pointer (1).
push 2
call g ; Call to g(2)
add esp, 4 ; Pop value 2 -- Remember that eax == ebx, now (they point to same address)
mov eax, [eax] ; Forget the pointer, store the value (4).
add eax, ebx ; Add our two values. 4 + 1 = 5
ret
因此,在使用这样的共享变量而不显式存储其值时,指令的顺序非常重要。通常,指令顺序将取决于编译器以及是否打开或关闭某些优化标志。此外,任何一个结果都可以被认为是一个合理的假设,没有硬性语义来管理这一点,因为它没有真正违反标准(更多:见Eric的回复如下):它是取消引用每个函数的返回值并添加结果。因此,如果编译器优化重新排序事情的方式,则会导致意外结果。