【彙編】C++ 函數調用之——有參無返回調用(傳值)

C++函數有參調用有幾種傳參方式: 函數

一.傳值 編碼

二.傳指針(地址) spa

三.傳引用 指針

其中參數可被const修飾,也能夠有默認值。下面分狀況討論: code

爲了簡潔,省略main函數的彙編碼而直接給出func函數的彙編碼。 ip

一.傳值調用 內存

有源代碼: class

void func(int a,char b){
	int c;
	c=a+b;
}

int main(int argc,char *argv[])
{
	//call func
	func(10,'a');
	return 0;
}
下面看看彙編碼:

調用發生時: 變量

//call func
	func(10,'a');
//進行參數壓棧操做,首先是'a'壓入棧,而後是10壓棧,而後call跳轉表,再由調轉表call函數
00F1141E  push        61h  
00F11420  push        0Ah  
00F11422  call        00F1113B  
//函數調用完成後,棧減少8字節,兩個dword,由於CPU對棧的操做都是雙字操做,這裏兩個參數就是兩個雙字
00F11427  add         esp,8
具體內存中的表現是這樣的(先讓func把棧初始化):

顯然不在func的stack內,注意兩個參數前面還有兩個DWORD, 引用

一個是00f1 1427,另外一個是00dd f794;這兩個DWORD的產生應該是在PUSH兩個參數以後,

又有的兩個PUSH,顯然,第一個PUSH 00f1 1427是在call 時將ip壓棧致使的:

這個ip是當前這條call 指令的下一條指令(add)的地址,請參考上面的main函數。

第二個PUSH是在 func函數中完成的,能夠參考func函數的彙編碼:

void func(int a,char b){

00F113D0  push        ebp  
//這裏第二個PUSH,壓入ebp,顯然這個ebp的值能夠在main函數裏面看到,
//有兩條:
//## 00F11401  mov   ebp,esp 
//## 00F11403  sub   esp,0C0h 
//那麼ebp就是main的棧底

00F113D1  mov         ebp,esp  
00F113D3  sub         esp,0CCh  
00F113D9  push        ebx  
00F113DA  push        esi  
00F113DB  push        edi  
00F113DC  lea         edi,[ebp+FFFFFF34h]  
00F113E2  mov         ecx,33h  
00F113E7  mov         eax,0CCCCCCCCh  
00F113EC  rep stos    dword ptr es:[edi]  
	int c;
	c=a+b;
00F113EE  movsx       eax,byte ptr [ebp+0Ch]  
00F113F2  add         eax,dword ptr [ebp+8]  
00F113F5  mov         dword ptr [ebp-8],eax  
}
00F113F8  pop         edi  
00F113F9  pop         esi  
00F113FA  pop         ebx  
00F113FB  mov         esp,ebp  
00F113FD  pop         ebp  
00F113FE  ret
調用發生時,壓入兩個參數後,必須再保存下一條指令的位置,所以有一個壓棧操做,這個操做是有call指令來完成的。 其次,func函數將ebp壓棧是爲了爲恢復堆棧作準備。由於CPU只有兩個寄存器用於堆棧操做:SS:SP,爲了調用func函數完成時能進入main的堆棧,必須先保存(push ebp)再恢復(pop ebp),這一點從func函數末尾也看得出。

此外,更直觀一點,從內存中看得出:第二個push 00ddf794和func的stack靠的很近:

剛好是指向了main的棧底。

再來看看func裏面:

int c;
	c=a+b;
00F113EE  movsx       eax,byte ptr [ebp+0Ch]  
00F113F2  add         eax,dword ptr [ebp+8]  
00F113F5  mov         dword ptr [ebp-8],eax  

//通過分析能夠知道:
//&b = ebp+0ch
//&a = ebp+8
//&c = ebp-8
//在上面的分析中咱們知道這個ebp是指向棧底的,局部變量c在棧內,參數a 和 b 是以前push進來的
通過上述分析,能夠得出一些結論:

有參函數調用發生時:

1.先將參數從右向左依次壓棧

2.將下一條指令的地址壓棧

3.被調函數將主調函數的棧底位置壓棧

4.被調函數初始化本身的棧

5.取出參數進行運算(並非pop)

6.恢復棧指針

7.執行ret恢復(pop)ip,此時程序轉到call的下一條add esp

8.向下移動棧頂指針sp,所謂的釋放局部變量。

能夠看到局部變量的"釋放"實際上是在主調函數中完成的,而不是在被調用函數末尾。

"釋放"不是清除內存,而是修改棧指針使局部變量不能訪問。

相關文章
相關標籤/搜索