2010-12-09 64 views
0
[cprg]$ cat test.c 
#include <stdio.h> 
#include <stdlib.h> 

int main(int argc,char *argv[]) 
{ 
     int i=10; 
     printf("i=%d\ni++=%d\n++i=%d\n",i,i++,++i); 
     return 0; 
} 
[cprg]$ make 
gcc -g -Wall -o test test.c 
test.c: In function ‘main’: 
test.c:7: warning: operation on ‘i’ may be undefined 
test.c:7: warning: operation on ‘i’ may be undefined 
[cprg]$ ./test 
i=12 
i++=11 
++i=12 

我不知道为什么会发生这种情况。请任何人 详细解释我在这里发生了什么?奇怪的printf行为?

+4

这不是`printf`是造成了奇怪的行为。 – 2010-12-09 17:18:45

+0

您应该意识到,编写测试程序(我希望这是一个测试,并且不用于生产代码)是很好的,并且很棒,但是在一个编译器上获得预期(或意外)结果并不能保证其他编译器的行为方式相同。 https://secure.wikimedia.org/wikipedia/en/wiki/Sequence_point阅读** C和C++中的顺序点**下的第4项** – Praetorian 2010-12-09 17:25:26

回答

6

C没有定义函数调用参数被评估的顺序。你在那里遇到麻烦;)。

更新:
为了澄清定义是什么,什么不可以:

功能标志的评估顺序,实际参数中的实际参数,并且 子表达式是不确定的,但有一个在实际调用之前的顺序点 。

从ISO/IEC 9899:1999,第6.5.2.2节,函数调用

+1

正确。查看http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points – Kos 2010-12-09 17:14:52

-2

它有ii++++i表达的评价为了做Ç。作为一个例子是可以的,但是在真实代码中,如果你想避免奇怪的问题,不要依赖这个顺序。

1

下面是主要的(部分需要)拆卸:

0x080483ed <+9>: movl $0xa,0x1c(%esp)   # initializes i 
0x080483f5 <+17>: addl $0x1,0x1c(%esp)   # i += 1 
0x080483fa <+22>: mov 0x1c(%esp),%edx  # edx = i = 11 
0x080483fe <+26>: addl $0x1,0x1c(%esp)   # i += 1 
0x08048403 <+31>: mov $0x80484f0,%eax   # address of string 
0x08048408 <+36>: mov 0x1c(%esp),%ecx  # ecx = i = 12 
0x0804840c <+40>: mov %ecx,0xc(%esp)   # pushes ecx (++i) 
0x08048410 <+44>: mov %edx,0x8(%esp)   # and edx (i++) 
0x08048414 <+48>: mov 0x1c(%esp),%edx  # now gets edx (i) 
0x08048418 <+52>: mov %edx,0x4(%esp)   # and pushes it 
0x0804841c <+56>: mov %eax,(%esp)   # push address of string 
0x0804841f <+59>: call 0x804831c <[email protected]> # write 

现在,由于参数都推在反向堆栈中,反汇编显示被推入的第一个是ecx,所以我们可以假定它是++ i(因为它是printf中的最后一个参数),所以edx是i ++。奇怪的是,它决定先计算出i ++,然后再计算++ i。最后,它加载我并推动它,但在这一点上,我已经增加了两次,所以它是12. 这真是一个未定义的行为!看:

printf("++i=%d\ni++=%d\ni=%d\n",++i,i++,i); 

生产:

++i=12 
i++=10 
i=12