【汇编】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,所谓的释放局部变量。

能够看到局部变量的"释放"实际上是在主调函数中完成的,而不是在被调用函数末尾。

"释放"不是清除内存,而是修改栈指针使局部变量不能访问。

相关文章
相关标签/搜索