2014-01-14 58 views
3

这是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)? (55.088.0

+0

这是未指定的或未定义的行为;这里没有明确的评估顺序。 –

+0

我相信奥利是正确的。为了更加明确,这将取决于添加之前的值如何存储。如果在存储该值之前执行'* g(1.0)',然后执行'* g(2.0)',您将添加'4.0 + 4.0 = 8.0'(请记住,每个指针指向相同静态变量的值)。否则,如果执行'* g(1.0)'并将其值存储在寄存器中,则执行'* g(2.0)'并添加结果,您将得到'1.0 + 4.0 = 5.0'。 – RageD

+0

[来自C faq的相关问题](http://c-faq.com/expr/evalorder2.html);基本上,如果要保证它们被调用的顺序,您需要将函数调用放在单独的语句中。 – hugomg

回答

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的,而不是浮动的):

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的回答如下):它是从每个功能并添加结果。因此,如果编译器优化重新完成事情的方式,这会导致意想不到的结果。

+0

C 2011中可能存在真实违规行为。第6条。第5条第2款似乎认为,如果对一个对象的不同副作用相互之间没有进行序列化,并且分配给静态对象的副作用相对于彼此是不相关的(对于同一对象),则行为是不确定的,因为函数调用评估的顺序没有确定。 –

+0

@EricPostpischil:感谢您指出这一点;我已更新我的帖子。 – RageD