i和i的效率差別_第1頁
i和i的效率差別_第2頁
i和i的效率差別_第3頁
i和i的效率差別_第4頁
i和i的效率差別_第5頁
已閱讀5頁,還剩3頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

i++和++i的效率差別一個無數人討論過的問題,今天終于看到一個人講得全面而清楚。下面這個帖子是shornmao(死貓)發的,我只是幫他貼過來而已,希望死貓不會生我的氣。首先聲明,簡單的比較前綴自增運算符和后綴自增運算符的效率是片面的,因為存在很多因素影響這個問題的答案。首先考慮內建數據類型的情況:如果自增運算表達式的結果沒有被使用,而僅僅簡單的用于增加一員操作數,答案是明確的,前綴法和后綴法沒有任何區別,編譯器的處理都應該是相同的,很難想象得出有什么編譯器實現可以別出心裁在二者之間制造任何差異。測試C++源代碼如下://test1.cppvoidtest(){inti=0;i++;++i;}GnuC/C++2編譯的匯編中間代碼如下:.file"test1.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpsubl$24,%espmovl$0,-4(%ebp);i=0incl-4(%ebp);i++incl-4(%ebp);++ijmpL3jmpL2.p2align4,,7L3:L2:leaveret很顯然,不管是i++還是++i都僅僅是一條incl指令而已。如果表達式的結果被使用,那么情況要稍微復雜一些。測試C++源代碼如下://test2.cppvoidtest(){inti=0,a,b;a=i++;b=++i;}GnuC/C++2編譯的匯編中間代碼如下:.file"test2.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpsubl$24,%espmovl$0,-4(%ebp);i=0movl-4(%ebp),%eax;i-->axmovl%eax,-8(%ebp);ax-->a(a=i)incl-4(%ebp);i++incl-4(%ebp);++imovl-4(%ebp),%eax;i-->axmovl%eax,-12(%ebp);ax-->b(b=i)jmpL3jmpL2.p2align4,,7L3:L2:leaveret有差別嗎?顯然也沒有,同樣是一條incl指令,再加上兩條movl指令借用eax寄存器復制調用棧內容。讓我們再加上編譯器優化,重新編譯后的匯編代碼如下:.file"test2.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpleaveret好了,優化的過火了,由于i,a,b三個變量沒有被使用,所以干脆全都被優化了,結果成了一個什么都不做的空函數體。那么,讓我們再加上一點代碼使用 a和b的結果吧,這樣i的結果也不能夠忽略了, C++源代碼如下://test3.cppinttest(){inti=0,a,b;a=i++;b=++i;returna+b;}此時匯編代碼如下:.file"test3.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpmovl$2,%eaxleaveret啟動,你還是沒有想到吧,答案僅僅是編譯器計算了返回值,常量展開(constant-unwinding)變成了直接返回常量結果。啟動,怎么辦?我們把i變成參數,避免這種預期以外的結果,C++源代碼如下://test4.cppinttest1(inti){inta=i++;returna;}inttest2(inti){inta=++i;returna;好了,很辛苦,終于得到了不一樣的匯編代碼:.file"test4.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test1__Fi.def_test1__Fi;.scl2;.type32;.endef_test1__Fi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxleaveret.align4.globl_test2__Fi.def_test2__Fi;.scl2;.type32;.endef_test2__Fi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxincl%eaxleaveret和你接觸到的教條正相反吧,++i反而增加了一條匯編指令incl,而i++卻沒有,這就是編譯器優化的魅力。因為不管i有沒有增加,都不影響a的值,而函數僅僅返回i的值,所以i的自增運算就根本不必進行了。所以,為了更客觀一些,我們將i參數改為按照引用傳遞,C++源代碼如下;//test5.cppinttest1(int&i){inta=i++;returna;}inttest2(int&i){inta=++i;returna;}這一次的結果加入了指針的運算,稍微復雜一些:.file"test5.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test1__FRi.def_test1__FRi;.scl2;.type32;.endef_test1__FRi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxmovl(%eax),%edxincl(%eax)movl%edx,%eaxleaveret.align4.globl_test2__FRi.def_test2__FRi;.scl2;.type32;.endef_test2__FRi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxmovl(%eax),%edxleal1(%edx),%ecxmovl%ecx,(%eax)movl%ecx,%eaxleaveret驚訝嗎?還是a=i++的代碼更高效一些,不知道這會讓你有什么想法。反正,我得出的結論,對于內建數據類型來說,i++和++i孰優孰劣,是編譯器實現相關的,實在不必太可以關心這個問題。最后讓我們再回到起點,對于自定義數據類型(主要是指類)說,不需要再做很多匯編代碼的分析了,我很清楚的知道為什么會有人循循善誘。因為前綴式可以返回對象的引用,而后綴式必須返回對象的值,所以導致了在大對象的時候產生了較大的復制開銷,引起效率降低,因此會有勸告盡量使用前綴式,盡可能避免后綴式,除非從行為上真的需要后綴式。這也就是MoreEffectiveC++/Term7中的原文提到的,處理使用者自定義類型(注意不是指內建類型)的時候,應該盡可能的使用前綴式地增 /遞減,因為他天生體質較佳。同時,為了保證前綴和后綴對遞增/遞減的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論