i++、++i、i=i+1、效率怎麼樣?看過一本書上說,i++比i=i+1好
的地方是因為i=i+1中的那個1要占用一個寄存器,是以速度沒有
i++快,于是我想驗證一下。另外,以前聽說過Java中的“i=i++”
得不到正确結論,也就是應該是“先累加再指派”,但Java經過這
種運算後,i值居然沒有變化。是以在這裡,想一并把這幾個問題
在C中驗證一下。
=====================測試的C源程式====================
#01: #include <stdio.h>
#02:
#03: main()
#04: {
#05: int i=0, j=0;
#06: i=i++;
#07:
#08: i=i+1;
#09: i++;
#10: ++i;
#11:
#12: j=i+1;
#13: j=i++;
#14: j=++i;
#15:
#16: printf("i=%d", i);
#17: }
======================================================
下面是我在 VC++ 6.0 + SP5 / Window 2000環境下對上述源程式的反彙編:
5: int i=0, j=0;
0040D698 mov dword ptr [ebp-4],0
0040D69F mov dword ptr [ebp-8],0
6: i=i++;
0040D6A6 mov eax,dword ptr [ebp-4]
0040D6A9 mov dword ptr [ebp-4],eax
0040D6AC mov ecx,dword ptr [ebp-4]
0040D6AF add ecx,1
0040D6B2 mov dword ptr [ebp-4],ecx
8: i=i+1;
0040D6B5 mov edx,dword ptr [ebp-4]
0040D6B8 add edx,1
0040D6BB mov dword ptr [ebp-4],edx
9: i++;
0040D6BE mov eax,dword ptr [ebp-4]
0040D6C1 add eax,1
0040D6C4 mov dword ptr [ebp-4],eax
10: ++i;
0040D6C7 mov ecx,dword ptr [ebp-4]
0040D6CA add ecx,1
0040D6CD mov dword ptr [ebp-4],ecx
12: j=i+1;
0040D6D0 mov edx,dword ptr [ebp-4]
0040D6D3 add edx,1
0040D6D6 mov dword ptr [ebp-8],edx
13: j=i++;
0040D6D9 mov eax,dword ptr [ebp-4]
0040D6DC mov dword ptr [ebp-8],eax
0040D6DF mov ecx,dword ptr [ebp-4]
0040D6E2 add ecx,1
0040D6E5 mov dword ptr [ebp-4],ecx
14: j=++i;
0040D6E8 mov edx,dword ptr [ebp-4]
0040D6EB add edx,1
0040D6EE mov dword ptr [ebp-4],edx
0040D6F1 mov eax,dword ptr [ebp-4]
0040D6F4 mov dword ptr [ebp-8],eax
=================================================================
下面是我在 SCO UNIX下用cc -g 對上述源程式編譯後,用dbx打出的記憶體彙編代碼:
( int i=0, j=0; )
0x0000015e (main:5) mov DWord Ptr [ebp-0x04],$0
0x00000165 (main:5) mov DWord Ptr [ebp-0x08],$0
( i=i++; )
0x0000016c (main:6) mov eax,DWord Ptr [ebp-0x04]
0x0000016f (main:6) inc DWord Ptr [ebp-0x04]
0x00000172 (main:6) mov DWord Ptr [ebp-0x04],eax
( i=i+1; )
0x00000175 (main:8) inc DWord Ptr [ebp-0x04]
( i++; )
0x00000178 (main:9) inc DWord Ptr [ebp-0x04]
( ++i; )
0x0000017b (main:10) inc DWord Ptr [ebp-0x04]
( j=i+1; )
0x0000017e (main:12) mov eax,DWord Ptr [ebp-0x04]
0x00000181 (main:12) inc eax
0x00000182 (main:12) mov DWord Ptr [ebp-0x08],eax
( j=i++; )
0x00000185 (main:13) mov eax,DWord Ptr [ebp-0x04]
0x00000188 (main:13) inc DWord Ptr [ebp-0x04]
0x0000018b (main:13) mov DWord Ptr [ebp-0x08],eax
( j=++i; )
0x0000018e (main:14) inc DWord Ptr [ebp-0x04]
0x00000191 (main:14) mov eax,DWord Ptr [ebp-0x04]
0x00000194 (main:14) mov DWord Ptr [ebp-0x08],eax
======================================================================
1、從上述的彙編代碼我們不難看出對于i=i+1; i++; ++i這三個指令的彙編
指令無論是在VC下還是在SCO UNIX的cc下都是一樣的(雖然這兩個編譯器
對其彙編得到的指令不太一樣,但是它們對這三條指令的彙編都是一樣的,
這裡,我是關閉編譯器優化的選項,也許現在的編譯器自動對之優化了)
在VC下都是:
0040D6B5 mov edx,dword ptr [ebp-4]
0040D6B8 add edx,1
0040D6BB mov dword ptr [ebp-4],edx
在cc下都是:
0x0000017b (main:10) inc DWord Ptr [ebp-0x04]
2、對于複合指令 j=i+1; j=i++; 卻有所不同,
在VC下:j=i+1 是三條指令,而 j=i++ 卻有五條指令,這也很合理。
在SCO下: j=i+1 和 j=i++ 都是三條指令。(j=i++指令數比VC少)
3、對于i=i++,在VC下可以得到正确的結果 i=1;而在SCO下卻是i=0; 這可以
從其彙編看到。
VC對i=i++的彙編是:
0040D6A6 mov eax,dword ptr [ebp-4] //取記憶體值i到eax
0040D6A9 mov dword ptr [ebp-4],eax //把eax值放到記憶體i中
0040D6AC mov ecx,dword ptr [ebp-4] //取記憶體值i到ecx
0040D6AF add ecx,1 //寄存器ecx加1
0040D6B2 mov dword ptr [ebp-4],ecx //把ecx值放到記憶體i中
SCO對i=i++的彙編是:
//取記憶體i到寄存器eax中
0x0000016c (main:6) mov eax,DWord Ptr [ebp-0x04]
//對記憶體i進行累加
0x0000016f (main:6) inc DWord Ptr [ebp-0x04]
//把寄存器eax的值放到記憶體i中
0x00000172 (main:6) mov DWord Ptr [ebp-0x04],eax
可見,之是以SCO得不到正确的結果的原因了。我已為其加上了注釋,相信各位是看得懂的。
【結論】:
1、如果是單語句,無論是i=i+1; i++; ++i;其效率是完全一樣的。
2、之是以SCO下的i=i++得不到正确結果,是因為其編譯器追求效率的結果。(Java亦如此)
3、觀察SCO下的彙編指令“inc DWord Ptr [ebp-0x04]”,難道可以直接操作記憶體嗎?(拿不準)
4、各個産商的編譯器各有不同,所産生的語句的指令代碼也有所不同,C++就更尤其如此啦。
最後附上Java對i=i++的測試
源程式:
#1: class Test
#2: {
#3: public static void main(String[] argv)
#4: {
#5: int i=0;
#6: i=i++;
#7: System.out.println("i="+i);
#8: }
#9: }
用javac -g Test.java 把其編譯成 Test.class。
再用javap -c Test打出其虛拟機指令代碼如下:
D:\>javap -c Test
Compiled from Test.java
class Test extends java.lang.Object {
Test();
public static void main(java.lang.String[]);
}
Method Test()
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 return
Method void main(java.lang.String[])
0 iconst_0
1 istore_1
2 iload_1
3 iinc 1 1
6 istore_1
7 getstatic #2 <Field java.io.PrintStream out>
10 new #3 <Class java.lang.StringBuffer>
13 dup
14 invokespecial #4 <Method java.lang.StringBuffer()>
17 ldc #5 <String "i=">
19 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
22 iload_1
23 invokevirtual #7 <Method java.lang.StringBuffer append(int)>
26 invokevirtual #8 <Method java.lang.String toString()>
29 invokevirtual #9 <Method void println(java.lang.String)>
32 return
可見其中的:
就是i=i++的虛拟機指令,想來一定和SCO的編譯器有異曲同工之處。
【備注】
這段程式的結果是i=0,我是在 j2se 1.4.0 下進行的測試。
雖然說,這是一個BUG,但是有多少人又會寫i=i++這種無聊的語句呢?
不過卻可以了解一下編譯器(解釋器)的工作方式。
本文轉自 haoel 51CTO部落格,原文連結:http://blog.51cto.com/haoel/124716,如需轉載請自行聯系原作者