1. 程式人生 > >i++與++i的區別+彙編分析

i++與++i的區別+彙編分析

這是很多初級C/C++程式設計師最容易碰到的一個問題

這裡來給大家簡明摘要的給大家講解一下:

i++

i++是後遞增

有如下語句:

int i = 0;
printf("%d,",i++);

在列印輸出時會發現是0,上面也說了i++是後遞增其意思就是在一條指令執行完成之後才會去對i這個變數進行遞增

printf("%d",i++);的執行流程如下:

先printf列印i的值,在列印完成之後也就是語句結束時對i進行遞增

我們可以做個試驗:

int i = 0;
printf("%d,",i++);
printf("%d",i);

列印結果:0,1

也就是證明了上面說的後遞增會在語句結束時對變數進行遞增

我們來看一下彙編程式碼(如果沒有學過彙編的可以直接跳過這一步驟)

	printf("%d", i++);
00C91C79  mov         eax,dword ptr [i]          
00C91C7F  mov         dword ptr [ebp-1C4h],eax  
00C91C85  mov         ecx,dword ptr [i]  
00C91C8B  add         ecx,1  
00C91C8E  mov         dword ptr [i],ecx  
00C91C94  mov         esi,esp  
00C91C96  mov         edx,dword ptr [ebp-1C4h]  
00C91C9C  push        edx  
00C91C9D  push        0C96858h  
00C91CA2  call        dword ptr ds:[0C9A120h]  
00C91CA8  add         esp,8  
00C91CAB  cmp         esi,esp  
00C91CAD  call        __RTC_CheckEsp (0C9115Eh)  

dword(雙字型別四位元組) ptr(指標指向地址)

dword ptr [i]指令意思就是指向一個四位元組的i地址

mov         eax,dword ptr [i]  

此時將eax暫存器中的值寫入到棧低指標-1C4h的地址當中去,

 ebp-1C4h記憶體地址是用來存放一些臨時值的!
mov         dword ptr [ebp-1C4h],eax  

通過mov和ptr指令的作用將位於記憶體中i的值寫入的累加器中(EAX暫存器,ALU運算單元所使用的暫存器)

mov         ecx,dword ptr [i]

最後在對暫存器裡的值進行遞增1

add         ecx,1  

然後寫入到裡面去

00C91C8E  mov         dword ptr [i],ecx  

最後這一段程式碼是呼叫printf函式的程式碼並將要列印輸出的值傳遞進去

00C91C96  mov         edx,dword ptr [ebp-1C4h]  
00C91C9C  push        edx  
00C91C9D  push        0C96858h  
00C91CA2  call        dword ptr ds:[0C9A120h]  

我們來分析一下

 mov         edx,dword ptr [ebp-1C4h]  

這行程式碼的作用是將棧中棧低指標-1C4h的值寫入到edx通用暫存器當中,注意此時低指標-1C4h的值是0,在最開始的彙編程式碼處也說過,在一開始編譯器就將i的值寫入到該記憶體中去了:

mov         eax,dword ptr [i]          
mov         dword ptr [ebp-1C4h],eax 
期間該記憶體值沒有任何變化

然後壓入新值到棧中

 push        edx  
 push        0C96858h  
呼叫printf函式並列印edx暫存器的值,每次呼叫printf,printf函式都會自動從edx暫存器中讀取要列印的資料
call        dword ptr ds:[0C9A120h]  

從上面的程式碼可以的值printf的入口地址位於記憶體中的0C9A120h當中,所以通過上面幾行彙編程式碼的分析也就得知為什麼i++在列印時不是遞增後的值了

注意printf每次編譯執行時地址都會發生改變,printf函式在編譯期間會被連結到檔案當中並分配一個新的檔案偏移記憶體對映地址!

下面在說一些最後的幾行彙編程式碼吧

給棧頂指標遞增8個位元組

add         esp,8  

cmp指令是減法操作,操作之後會設定標誌位暫存器所以也就是判斷指令,注意進行減法運算後並不會對兩個暫存器裡的值產生任何影響,結果會存放到通用暫存器當中,並根據通用暫存器的值來設定標誌位!

判斷變址暫存器與esp地址

cmp         esi,esp  

後面的彙編指令中沒有任何判斷標誌位的指令,所以這裡就只是呼叫一下cmp指令,並沒有做任何判斷!

call        __RTC_CheckEsp (0C9115Eh)  

__RTC_CheckEsp函式是檢查某記憶體緩衝區是否溢位的

呼叫此函式檢查0C9115Eh記憶體緩衝區是否溢位!

++i就非常簡單了

int i = 0;
printf("%d",++i);

列印結果:1

++i為前遞增,也就是先對其進行遞增後使用printf列印輸出

彙編程式碼:

013A1CB2  mov         eax,dword ptr [i]  
013A1CB8  add         eax,1  
013A1CBB  mov         dword ptr [i],eax  
013A1CC1  mov         esi,esp  
013A1CC3  mov         ecx,dword ptr [i]  
013A1CC9  push        ecx  
013A1CCA  push        0C96858h
013A1CCF  call        dword ptr ds:[0C9A120h]  
013A1CD5  add         esp,8  
013A1CD8  cmp         esi,esp  
013A1CDA  call        __RTC_CheckEsp (0C9115Eh)  
這兩行程式碼可以看出來,直接將i的值寫入到eax暫存器中,並直接遞增1,也不寫入到ebp-1C4h棧地址中臨時儲存了
mov         eax,dword ptr [i]  
add         eax,1 

最後直接將i的值寫入到ecx暫存器中,並把ecx暫存器的值壓入棧,然後呼叫printf函式列印輸出

mov         ecx,dword ptr [i]  
push        ecx  
push        0C96858h
call        dword ptr ds:[0C9A120h]  
從彙編程式碼可以分析出,++i要快於i++,因為++i無需寫入到ebp-1C4h棧地址中儲存資料,而是直接對i本身記憶體地址進行操作!