VC++函數參數壓棧順序

今天閒來無事寫了一段代碼:
int Func1(int x,int y){
	return (x&y)+((x^y)>>1);
}
void main(){
        int Array[] = {1,2,3,4,5};
	int *ptr = Array;
	int c = Func1(*ptr,*(++ptr));
	printf("%d\n",c);
}

原本預想輸出結果應該爲1,誰知竟然爲2,百思不得姐之後,就調試了下進入反彙編查看其中奧妙:

55:       int Array[] = {1,2,3,4,5};
00401088   mov         dword ptr [ebp-14h],1
0040108F   mov         dword ptr [ebp-10h],2
00401096   mov         dword ptr [ebp-0Ch],3
0040109D   mov         dword ptr [ebp-8],4
004010A4   mov         dword ptr [ebp-4],5
這個過程就不用解釋了,預先計算數組大小,數組按內存地址從低位到高位分配,接下來:
56:       int *ptr = Array;
004010AB   lea         eax,[ebp-14h]
004010AE   mov         dword ptr [ebp-18h],eax
注意mov和lea的區別,如果你認爲lea eax,[ebp-18h]是將內存地址爲ebp-14h中的內容賦給eax那就錯了,lea只是將內存地址賦給eax,也就是此時eax的內容是ebp-14h。對於指令mov eax,[ebp-14h]自然是將內存中的地址壓入eax。說到這裏就囉嗦一下,mov的右值必須爲常量不能爲表達式,如,可以寫MOV EAX, EBP,但不能寫MOV EAX, EBP + 8這是因爲EBP + 8本身也需要一條指令來計算,所以不能跟MOV寫在一條指令裏。但是彙編指令中內存地址符可以做算術運算,所以對於指令MOV EAX, [EBP + 8]完全是合法的。接下來繼續跟下一條指令:

57:       int c = Func1(*ptr,*(++ptr));
004010B1   mov         ecx,dword ptr [ebp-18h] //ecx賦值爲ebp-14h
004010B4   add         ecx,4  //ecx值爲ebp-10h,指向Array[1]
004010B7   mov         dword ptr [ebp-18h],ecx  
004010BA   mov         edx,dword ptr [ebp-18h]  
004010BD   mov         eax,dword ptr [edx]   //Array[1]壓入eax
004010BF   push        eax   //2
004010C0   mov         ecx,dword ptr [ebp-18h]
004010C3   mov         edx,dword ptr [ecx] //ecx值爲ebp-10h,指向Array[1]
004010C5   push        edx  //2
004010C6   call        @ILT+0(Func1) (00401005)
004010CB   add         esp,8  
004010CE   mov         dword ptr [ebp-1Ch],eax
根據以上指令我們可以判斷該函數參數的入棧順序爲從右至左,所以先執行了++ptr指令,我們繼續往下跟:

58:       printf("%d\n",c);
004010D1   mov         eax,dword ptr [ebp-1Ch]
004010D4   push        eax
004010D5   push        offset string "%d\n" (0043101c)
004010DA   call        printf (004081f0)
004010DF   add         esp,8
很明顯,ebp-1Ch爲局部變量c的地址,在printf中居然也是先將c壓棧,然後纔是那一串格式控制字符串,這個平時居然沒有注意,那麼對於以下代碼該輸出多少呢?int i=0;printf("%d,%d,%d",++i,i,--i);輸出該是0,-1,-1,而不是想當然的1,1,0。這個問題可以繼續深入的探討下去,通過查閱資料,得到如下結論:

1. 參數入棧順序是和具體 編譯器實現相關的。比如,Pascal語言中參數就是從左到右入棧的,有些語言中還可以通過修飾符進行指定,如Visual C++。

2. Pascal語言不支持可變長參數,而C語言支持這種特色,正是這個原 因使得C語言函數參數入棧順序爲從右至左。具體原因爲:C方式參數入棧順序(從右至左)的好處就是可以動態變化參數個數。

3. 這裏面還牽涉到一個新的問題:標準調用_stdcall和C調用_cdecl,這兩種調用方式可等到以後用到時在仔細區別。

最後,對於Func1函數的功能做個說明,這是個面試據說會考到的問題,它的作用是求兩輸入參數的平均值。

完-----------------------------^o^








發佈了36 篇原創文章 · 獲贊 18 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章