自增運算子的副作用

來源:互聯網
上載者:User

原文地址:http://www.titilima.cn/show-234-1.html

結論:

vs2008 一、先計算完所有的++i 將i的結果放入i中 二、電腦運算式的值 三、再計算所有的i++

G++也是先忽略i++,不過和vs2008區別之處在於不是先計算完所有的++i,再電腦運算式,而只是對每一個運算子先計算++i

好了,就從最為臭名昭著的“(++i) + (++i) + (++i)”開始吧。

C++代碼
int i = 4;   
int a = (++i) + (++i) + (++i);  
題目要求是求a的值,多見於各種等級考試、期末考試的選擇題。
顯然,這道題的考點是首碼自增運算子。與之相似的還有尾碼自增(減)或前尾碼增減混合的情況。
一牆之隔,圍城內外。在象牙塔外的世界,這個題目是最早遭到詬病者之一。因為,出題者所預設的程式運行環境是Turbo C,所以標準答案自然也就是TC的運行結果(有TC的朋友們不妨試一試,看看TC的結果是不是你那捲子上的標準答案)。而事實上,對於這個題目的結果,a 的值是無法預期的——C/C++標準規定,三個++i的子運算式是沒有求值順序點的,同時它們又是有副作用的,因此語言本身並不能保證副作用的順序。
眼見為實,讓我們來看看三款不同編譯器產生的代碼吧。我為每個必要的細節加了注釋,以便理解。

彙編代碼
; Visual C++ 6.0 sp6:   
mov [ebp+i], 4   ; i = 4   
mov eax, [ebp+i]   
add eax, 1       ; ++i, i == 5   
mov [ebp+i], eax   
mov ecx, [ebp+i]   
add ecx, 1       ; ++i, i == 6   
mov [ebp+i], ecx   
mov edx, [ebp+i]   
add edx, [ebp+i] ; 6 + 6 == 12   
mov eax, [ebp+i]   
add eax, 1       ; ++i, i == 7   
mov [ebp+i], eax   
add edx, [ebp+i] ; 12 + 7 == 19   
mov [ebp+a], edx ; a = 19   
  
; Visual Studio 2005:   
mov [ebp+i], 4   ; i = 4   
mov eax, [ebp+i]   
add eax, 1       ; ++i, i == 5   
mov [ebp+i], eax   
mov ecx, [ebp+i]   
add ecx, 1       ; ++i, i == 6   
mov [ebp+i], ecx   
mov edx, [ebp+i]   
add edx, 1       ; ++i, i == 7   
mov [ebp+i], edx   
mov eax, [ebp+i]   
add eax, [ebp+i] ; 7 + 7 == 14   
add eax, [ebp+i] ; 14 + 7 == 21   
mov [ebp+a], eax ; a = 21   
  
; gcc.exe (GCC) 3.4.5 (mingw-vista special):   
mov [ebp+i], 4      ; i = 4   
lea eax, [ebp+i]   
inc dword ptr [eax] ; ++i, i == 5   
lea eax, [ebp+i]   
inc dword ptr [eax] ; ++i, i == 6   
mov eax, [ebp+i]   
mov edx, [ebp+i]   
add edx, eax        ; 6 + 6 == 12   
lea eax, [ebp+i]   
inc dword ptr [eax] ; ++i, i == 7   
mov eax, edx   
add eax, [ebp+i]    ; 12 + 7 == 19   
mov [ebp+a], eax    ; a = 19  
——其實我大可不必列出如是這般冗長的彙編代碼,而只需要一個a值結果的總結表格就可以說明問題。不過我還是選擇了組合語言,原因有二:第一,任何的磚家、叫獸告訴你的東西都遠遠不及最終產生的目標代碼可靠;第二,使用彙編代碼可以把自己偽裝成高手,用來裝B的效果肯定比簡單的表格來得有效,何樂而不為哉。
裝都裝了,自然不怕遭雷劈。再來一個嵌入式裝置上的代碼,環境是eMbedded Visual C++ 4.0 sp4的ARMV4編譯器:

彙編代碼
MOV R0, #4        ; i = 4   
STR R0, [SP,#8+i]   
LDR R1, [SP,#8+i]   
ADD R0, R1, #1    ; ++i, i == 5   
STR R0, [SP,#8+i]   
LDR R1, [SP,#8+i]   
ADD R0, R1, #1    ; ++i, i == 6   
STR R0, [SP,#8+i]   
LDR R1, [SP,#8+i]   
ADD R0, R1, #1    ; ++i, i == 7   
STR R0, [SP,#8+i]   
LDR R1, [SP,#8+i]   
LDR R0, [SP,#8+i]   
ADD R2, R1, R0    ; 7 + 7 == 14   
LDR R3, [SP,#8+i]   
ADD R0, R2, R3    ; 14 + 7 == 21   
STR R0, [SP,#8+a] ; a = 21  
相信到這裡諸位都看到了,一個運算式在不同的編譯器上會出現不同的結果——特別是微軟的VC6和VS2005,一家產的編譯器的結果也是不一樣的。亦即是說,倘使你寫下了諸如“(++i) + (++i) + (++i)”這樣的代碼,你得到的結果將是一個無法預期的結果,必須的。
末了,說點八卦的。很多程式員將這種病態、晦澀的編碼方式歸咎於譚浩強版的《C程式設計》,認為譚老爺子是這種學究代碼的始作俑者。李馬饒有興緻地考證了一番,發現譚老爺子在《C程式設計》(第二版)的第58~59頁中對這種情況進行了討論,並指出以下幾點:

應該避免++/--的副作用可能產生的歧義性,建議將這樣的運算式拆開寫。
對於i+++j的情況,應使用括弧來使代碼明晰以避免誤解,如(i++)+j或i+(++j)。
“總之,不要寫出別人看不懂的、也不知道系統會怎樣執行的程式。”
竊為譚老爺子鳴不平啊。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.