随着c语言学习的深入,慢慢的对函数的调用有了些了解,为了更深层次的理解函数的调用,特此写了一篇博客,希望与大家交流学习,弥补自己的不足。
我们知道每一次的函数调用都是一个过程,而我们讲这个过程称之为(函数的调用过程)
这个过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存、现场保护。我们将这块栈空间称之为(函数栈帧)
*注意*:由于使用的编译器不同,产生的效果有所差异。我使用的是VS2013
下面我们来看一段代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int ret = Add(a, b);
printf("ret=%d\n", ret);
system("pause");
return 0;
}
在栈帧的维护我们要了解ebp和esp两个寄存器。在函数调用的过程中这两个寄存器存放了维护这个栈的栈底和栈顶指针
首先我们来看main函数栈帧的创建
int main()
{
000E1410 push ebp 1
000E1411 mov ebp,esp 2
000E1413 sub esp,0E4h 3
000E1419 push ebx 5
000E141A push esi .
000E141B push edi .
000E141C lea edi,[ebp-0E4h] 6
000E1422 mov ecx,39h .
000E1427 mov eax,0CCCCCCCCh .
000E142C rep stos dword ptr es:[edi] .
int a = 10; 6
000E142E mov dword ptr [a],0Ah .
int b = 20; .
000E1435 mov dword ptr [b],14h .
int ret = Add(a, b); 7
000E143C mov eax,dword ptr [b] .
000E143F push eax .
000E1440 mov ecx,dword ptr [a] .
000E1443 push ecx .
000E1444 call _Add (0E10E1h) 8
000E1449 add esp,8 .
000E144C mov dword ptr [ret],eax 9
printf("ret=%d\n", ret);
1、
000E1410 push ebp
//将ebp压栈处理,ebp储存指向栈底的地址,方便函数返回的恢复
2、
000E1411 mov ebp,esp
//将esp的值赋给ebp,产生新的ebp
3、
000E1413 sub esp,0E4h
//给esp减去一个16进制数字0E4h
4、
000E1419 push ebx
000E141A push esi
000E141B push edi
// ebx esi edi 依次进栈,esp的位置随之依次上升
5、
000E141C lea edi,[ebp-0E4h]
000E1422 mov ecx,39h
000E1427 mov eax,0CCCCCCCCh
000E142C rep stos dword ptr es:[edi]
//这四条指令是将栈帧预开辟空间全部初始化为0CCCCCCCCh
6、
int a = 10;
000E142E mov dword ptr [a],0Ah
int b = 20;
000E1435 mov dword ptr [b],14h
//处理局部变量a,b的创建
7、
int ret = Add(a, b);
000E143C mov eax,dword ptr [b]
000E143F push eax
//将传给Add函数的参数b赋给eax,然后再压栈
000E1440 mov ecx,dword ptr [a]
000E1443 push ecx
//将传给Add函数的参数b赋给ecx,然后再压栈
8、
000E1444 call _Add (0E10E1h)
//call指令的调用,要先进行压栈
000E1449 add esp,8
//call指令下一条指令的地址,然后跳转到ADD函数的地址,调用完函数又要返回到这个地址
9、
000E144C mov dword ptr [ret],eax
//将返回的值赋给ret
printf(“ret=%d\n”, ret);
000E1455 push 0E5858h
以上介绍了函数的赋值,函数的初始化,调用的新的函数的过程
.
.
.
.
.
下面介绍Add函数栈帧的创建
int Add(int x, int y)
{
000E13C0 push ebp 1
000E13C1 mov ebp,esp
000E13C3 sub esp,0CCh
000E13C9 push ebx
000E13CA push esi
000E13CB push edi
000E13CC lea edi,[ebp-0CCh]
000E13D2 mov ecx,33h
000E13D7 mov eax,0CCCCCCCCh
000E13DC rep stos dword ptr es:[edi] 2
int z = 0;
000E13DE mov dword ptr [z],0 3
z = x + y;
000E13E5 mov eax,dword ptr [x] 4
000E13E8 add eax,dword ptr [y] .
000E13EB mov dword ptr [z],eax 5
return z;
000E13EE mov eax,dword ptr [z] 6
}
000E13F1 pop edi
000E13F2 pop esi
000E13F3 pop ebx
1—>2过程类似,在此不在重复、
3、
000E13DE mov dword ptr [z],0
//创建变量z
4、
000E13E5 mov eax,dword ptr [x]
000E13E8 add eax,dword ptr [y]
//获取形参的a和b,并相加
5、
000E13EB mov dword ptr [z],eax
//将结果储存到z中
6、
000E13EE mov eax,dword ptr [z]
//将结果存储到eax寄存器,通过寄存器待会函数的返回值