天天看點

Inside i++

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,如需轉載請自行聯系原作者