奇怪的printf行为?

[cprg]$ cat test.c #include  #include  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 

我不知道为什么会发生这件事。 请问有谁能详细解释一下这里发生了什么?

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

更新:
澄清什么是定义的,什么不是:

函数指示符的评估顺序,实际参数和实际参数中的子表达式是未指定的,但在实际调用之前有一个序列点。

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

这是main(我们需要的部分)的反汇编:

 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  # write 

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

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

生产:

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

有关参数评估顺序的更多信息,请查看此StackOverflow链接:

C ++中编译器和评估的参数顺序

它与Cii++++i表达式的评估顺序有关。 作为一个例子是可以的,但如果你想避免奇怪的问题,在实际代码中不依赖于该顺序。