2010-12-09 80 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