函数的调用过程,栈桢的创建和销毁。

本文通过一个简单的C语言程序示例,详细解析了函数调用过程中栈桢的创建和销毁过程,包括如何在栈上分配空间、局部变量的初始化、参数传递以及返回值的处理等关键步骤。

                 函数的调用过程,栈桢的创建和销毁

我们首先要知道关于函数的调用,我们知道main函数也是要被调用的,在_tmainCRTStartup函数中调用,而_tmainCRTStartup函数是在mainCRTStartup中被调用的。

函数调用过程要为函数开辟栈空间,用于本次函数调用中临时变量的保存,称为函数栈桢

下面我用一个简单的程序说明函数的调用过程:

#include<stdio.h>
#include<string>
int Add(int x, int y)
{
	int sum = 0;
	sum = x + y;
	return sum;
}
int main()
{
	int a = 1;
	int b = 2;
	int ret = 0;
	ret = Add(a, b);
	printf("%d", ret);
	system("pause");
	return 0;
}

在这个程序里,主函数定义了3个局部变量,然后调用了Add函数。三个局部变量都在栈空间上存放。

可是当程序运行起来后,main函数是如何实现对Add函数的调用过程呢?我们转到反汇编一步步分析一下。

下面是main函数的汇编代码(在VS2013平台下):

int main()
{
009253D0  push        ebp  
009253D1  mov         ebp,esp  
009253D3  sub         esp,0E4h  
009253D9  push        ebx  
009253DA  push        esi  
009253DB  push        edi  
009253DC  lea         edi,[ebp-0E4h]  
009253E2  mov         ecx,39h  
009253E7  mov         eax,0CCCCCCCCh  
009253EC  rep stos    dword ptr es:[edi]  
	int a = 1;
009253EE  mov         dword ptr [a],1  
	int b = 2;
009253F5  mov         dword ptr [b],2  
	int ret = 0;
009253FC  mov         dword ptr [ret],0  
	ret = Add(a, b);
00925403  mov         eax,dword ptr [b]  
00925406  push        eax  
00925407  mov         ecx,dword ptr [a]  
0092540A  push        ecx  
0092540B  call        Add (0921113h)  
00925410  add         esp,8  
00925413  mov         dword ptr [ret],eax  
	printf("%d", ret);
00925416  mov         esi,esp  
00925418  mov         eax,dword ptr [ret] 
0092541B  push        eax  
0092541C  push        92CC7Ch  
00925421  call        dword ptr ds:[93019Ch]  
00925427  add         esp,8  
0092542A  cmp         esi,esp  
0092542C  call        __RTC_CheckEsp (09212E4h)  
	system("pause");
00925431  mov         esi,esp  
00925433  push        92CD34h  
00925438  call        dword ptr ds:[930184h]  
0092543E  add         esp,4  
00925441  cmp         esi,esp  
00925443  call        __RTC_CheckEsp (09212E4h)  
	return 0;
00925448  xor         eax,eax  
}
0092544A  pop         edi  
}
0092544B  pop         esi  
0092544C  pop         ebx  
0092544D  add         esp,0E4h  
00925453  cmp         ebp,esp  
00925455  call        __RTC_CheckEsp (09212E4h)  
0092545A  mov         esp,ebp  
0092545C  pop         ebp  
0092545D  ret  

1、从main函数的调用开始,需要为main函数创建函数栈桢。

int main()
{
009253D0  push        ebp       //将ebp压入栈底
009253D1  mov         ebp,esp   //将esp的值赋给ebp,得到新的ebp
009253D3  sub         esp,0E4h   //将esp的值减去0E4h,得到新的esp
009253D9  push        ebx        //从低地址到高地址三次压栈压入三个寄存器ebx、
009253DA  push        esi       //esi、                                                                                          009253DB  push        edi       //edi
009253DC  lea         edi,[ebp-0E4h]  //加载有效地址
009253E2  mov         ecx,39h           //将加载到edi的地址重复拷贝ecx次,
009253E7  mov         eax,0CCCCCCCCh    //即把main函数预开辟的空间全部初始化为0CCCCCCCCh
009253EC  rep stos    dword ptr es:[edi]  //重复拷贝
	int a = 1;
009253EE  mov         dword ptr [a],1   //处理局部变量a,把1赋给a
	int b = 2;
009253F5  mov         dword ptr [b],2  // 处理局部变量b,把2赋给b
	int ret = 0;
009253FC  mov         dword ptr [ret],0  
	ret = Add(a, b);
00925403  mov         eax,dword ptr [b]  
00925406  push        eax  
00925407  mov         ecx
初始化如下,调试可知:

2、Add函数的调用,参数传递过程如下:

	int ret = 0;
011C540C  mov         dword ptr [ret],0   //ret的初始化
	ret = Add(a, b);
011C5413  mov         eax,dword ptr [b]   //将b的值赋给eax,压栈eax,形参b的实例化
011C5416  push        eax  
011C5417  mov         ecx,dword ptr [a]    //将a的值赋给ecx,压栈ecx,形参a的实例化
011C541A  push        ecx  
011C541B  call        Add (011C1113h)     //调用,压栈call指令的下一条指令的地址,再跳转到Add函数的地方
011C5420  add         esp,8  
011C5423  mov         dword ptr [ret],eax  
	printf("%d", ret);

继续调试,进入Add函数:

#include<stdio.h>
#include<string>
int Add(int x, int y)
{
011C3860  push        ebp             //压入ebp
011C3861  mov         ebp,esp          //将esp的值赋给ebp
011C3863  sub         esp,0CCh        //将esp的值减去0CCh
011C3869  push        ebx  
011C386A  push        esi  
011C386B  push        edi  
011C386C  lea         edi,[ebp-0CCh]   //加载有效地址
011C3872  mov         ecx,33h  
011C3877  mov         eax,0CCCCCCCCh    //初始化开辟的空间
011C387C  rep stos    dword ptr es:[edi]    //重复拷贝
	int sum = 0;
011C387E  mov         dword ptr [sum],0    //创建sum,并初始化
	sum = x + y;
011C3885  mov         eax,dword ptr [x]     //将形参x的值赋给eax
011C3888  add         eax,dword ptr [y]    //将形参y的值赋给eax
011C388B  mov         dword ptr [sum],eax    //将相加后的值存储到sum中
	return sum;
011C388E  mov         eax,dword ptr [sum]   //将sum的值赋给eax,即将结果存储到寄存器中
}
011C3891  pop         edi            //出栈
011C3892  pop         esi            //出栈 
011C3893  pop         ebx            //出栈                                                                                      011C3894  mov         esp,ebp        //将ebp的值赋给esp,使esp向下移动
011C3896  pop         ebp            //出栈,将出栈的内容保存在ebp中,回到main的栈桢
011C3897  ret                        //出栈一次,并将出栈的内容当做地址,将程序执行跳转到该地址处

跳转到Add函数处,



以上是简单的过程介绍,下面是栈桢创建的模拟过程图:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值