c语言函数的调用过程分析是什么

一、函数的调用过程 首先我们再viso studio中编写如下代码: int add(int a, int b) { int c = a + b; return c; } int main() { int d; d = add(1,2); printf("d=%d\r\n",d); }

一、函数的调用过程

首先我们再viso studio中编写如下代码:

int add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int d;
	d = add(1,2);
	printf("d=%d\r\n",d);
}

反汇编后得到如下汇编代码:

int add(int a, int b)
{
//入栈保护现场
00B117A0  push        ebp  
00B117A1  mov         ebp,esp  
00B117A3  sub         esp,0CCh  
00B117A9  push        ebx  
00B117AA  push        esi  
00B117AB  push        edi  
00B117AC  lea         edi,[ebp-0CCh]  
00B117B2  mov         ecx,33h  
00B117B7  mov         eax,0CCCCCCCCh  
00B117BC  rep stos    dword ptr es:[edi]  
00B117BE  mov         ecx,offset _549F54B0_consoleapplication1@cpp (0B1C008h)  
00B117C3  call        @__CheckForDebuggerJustMyCode@4 (0B1120Dh)  
	int c = a + b;
00B117C8  mov         eax,dword ptr [a]  
00B117CB  add         eax,dword ptr [b]  
	int c = a + b;
00B117CE  mov         dword ptr [c],eax  
	return c;
00B117D1  mov         eax,dword ptr [c]  
}
//add调用结束,出栈恢复现场
00B117D4  pop         edi  
00B117D5  pop         esi  
00B117D6  pop         ebx  
00B117D7  add         esp,0CCh  //回复sp指针,释放局部变量占用的内存
00B117DD  cmp         ebp,esp  
00B117DF  call        __RTC_CheckEsp (0B11217h) 
00B117E4  mov         esp,ebp  
00B117E6  pop         ebp  
00B117E7  ret 

int main()
{
00CC1930  push        ebp   //基址指针寄存器
00CC1931  mov         ebp,esp  //保存当前堆栈,将栈顶指针存入ebp,当前栈顶和栈低重合
00CC1933  sub         esp,0CCh  //栈顶指针向下偏移0xccbyte,开辟临时变量的空间(栈空间)
00CC1939  push        ebx  
00CC193A  push        esi  
00CC193B  push        edi  
00CC193C  lea         edi,[ebp-0CCh]  
00CC1942  mov         ecx,33h  
00CC1947  mov         eax,0CCCCCCCCh  
00CC194C  rep stos    dword ptr es:[edi]  
00CC194E  mov         ecx,offset _549F54B0_consoleapplication1@cpp (0CCC008h)  
00CC1953  call        @__CheckForDebuggerJustMyCode@4 (0CC1221h)  
	int d;
	d = add(1,2);
//从这里可以看出来,函数参数的入栈方向是自有向左
00CC1958  push        2  //最右边的形参入栈
00CC195A  push        1  //最左边的形参入栈
00CC195C  call        add (0CC1186h)  
00CC1961  add         esp,8  
00CC1964  mov         dword ptr [d],eax  
	printf("d=%d\r\n",d);
00CC1967  mov         eax,dword ptr [d]  
00CC196A  push        eax  
00CC196B  push        offset string "d=%d\r\n" (0CC7B30h)  
00CC1970  call        _printf (0CC104Bh)  
00CC1975  add         esp,8  
}
//main函数调用结束,恢复现场
00CC1978  xor         eax,eax  
00CC197A  pop         edi  
00CC197B  pop         esi  
00CC197C  pop         ebx  
00CC197D  add         esp,0CCh  
00CC1983  cmp         ebp,esp  
00CC1985  call        __RTC_CheckEsp (0CC122Bh)  
00CC198A  mov         esp,ebp  
00CC198C  pop         ebp  
00CC198D  ret  

寄存器知识讲解

ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

X86/64 vc++的堆栈是从高地址向低地址生长,比如push一个32位寄存器入栈后,esp - 04H。

汇编指令讲解

sub 减计数器sub esp,04h表示将栈顶指针向下移动4个字节

总结

从上述C语言代码的反汇编来看,函数在调用的时候会对add的两个参数进行入栈操作,因此函数的形参也是需要占用栈空间的,只是形式参数占用的空间在函数调用结束后会自动释放。

函数的调用其实就是一个入栈和出栈的过程,在函数调用前调用push保护现场,在函数调用结束后调用pop恢复现场,并将sp指针恢复为函数调用前的状态,释放调用函数所占用的内存空间。

知秋君
上一篇 2024-08-13 18:02
下一篇 2024-08-13 17:36

相关推荐