C语言--探索函数栈帧的创建与销毁

目录

为main函数开辟栈帧 

创建变量

传参

为自定义函数开辟栈帧  

返回


        局部变量是怎么创建的?为什么局部变量的值是随机值?函数是怎么传参的?形参与实参的关系?函数怎么调用与返回?

我们用VS2013的环境进行探索(不同编译器下函数调用过程中栈帧的创建略有差异)。

        需要知道的常识:1.寄存器(eax,ebx,ecx,edx,ebp,esp...)。其中ebp,esp这两个寄存器是存放地址的,这两个地址用来维护函数栈帧。2.每一个函数调用,都要在栈上开辟一块空间。

我们用一段简单的代码进行演示。

int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	
	c = Add(a, b);

	printf("%d\n", c);

	return 0;
}

C语言--探索函数栈帧的创建与销毁_第1张图片

 C语言--探索函数栈帧的创建与销毁_第2张图片

 我们发现main函数被调用了?那么是被谁调用的呢?

C语言--探索函数栈帧的创建与销毁_第3张图片

我们可以清楚得看到main函数也是别的函数调用的。

因此:在vs2013中main函数也是被别的函数调用的,它们的关系是:

C语言--探索函数栈帧的创建与销毁_第4张图片

 我们转到反汇编

C语言--探索函数栈帧的创建与销毁_第5张图片

为main函数开辟栈帧 

第一步是push ebp,并且esp也改变了,我们在监视窗口可以看到:

C语言--探索函数栈帧的创建与销毁_第6张图片

 显而易见,esp减少了4。再看内存:

C语言--探索函数栈帧的创建与销毁_第7张图片

ebp确实压进了内存。

第二步:将esp的位置移动到ebp。

C语言--探索函数栈帧的创建与销毁_第8张图片

 第三步:将esp减去一个值。

C语言--探索函数栈帧的创建与销毁_第9张图片

 这样就为main函数与开辟好了一块空间。

C语言--探索函数栈帧的创建与销毁_第10张图片

 第四步:压进去三个寄存器(先不管,后面会弹掉).

C语言--探索函数栈帧的创建与销毁_第11张图片 

 

C语言--探索函数栈帧的创建与销毁_第12张图片

 内存窗口也可以清晰得看到压进去了三个。

第五步:lea(load effecitve address)加载有效地址,把一个地址加载到edi里面去。

C语言--探索函数栈帧的创建与销毁_第13张图片(显示符号名勾选)

 ebp-0E4h是黑箭头指向的位置。

C语言--探索函数栈帧的创建与销毁_第14张图片

 

 

 第六步:从edi开始,向下的39h个dword(4字节空间)的数据,即每次初始化4个字节,总共39h次,改成eax的内容(CCCCCCCC)

C语言--探索函数栈帧的创建与销毁_第15张图片

 ​​​​​​​C语言--探索函数栈帧的创建与销毁_第16张图片

 

  到这我们就要开始执行有效代码了,把0A放到ebp-8的位置。

C语言--探索函数栈帧的创建与销毁_第17张图片

 C语言--探索函数栈帧的创建与销毁_第18张图片

 内存中也确实如此。所以如果变量不初始化就是随机值。

创建变量

再接下来就是创建b变量。

C语言--探索函数栈帧的创建与销毁_第19张图片

 

 C语言--探索函数栈帧的创建与销毁_第20张图片

 同样创建c变量。

C语言--探索函数栈帧的创建与销毁_第21张图片

 C语言--探索函数栈帧的创建与销毁_第22张图片

传参

把ebp-14h(b  20)放到eax中,压栈。然后同样的方式处理b。(传参)

C语言--探索函数栈帧的创建与销毁_第23张图片

 下面执行call(调用)指令,我们要记住call指令的地址,执行后会把call指令的下一条指令的地址压栈。

C语言--探索函数栈帧的创建与销毁_第24张图片

 C语言--探索函数栈帧的创建与销毁_第25张图片

 ​​​​​​​C语言--探索函数栈帧的创建与销毁_第26张图片

为自定义函数开辟栈帧  

再往下执行,我们进入Add函数,前期建立栈帧和main函数一样。

C语言--探索函数栈帧的创建与销毁_第27张图片

 给一块空间给变量z,并初始化为0;

C语言--探索函数栈帧的创建与销毁_第28张图片

 C语言--探索函数栈帧的创建与销毁_第29张图片

 下面就开始执行z=x+y了。把ebp+8 (a')的值给eax,再把eax的值加上ebp-0Ch处的值,即eax的值为30,再将eax的值移动到ebp-8的位置,即完成了c=30.

C语言--探索函数栈帧的创建与销毁_第30张图片

 所以在调用函数前,参数已经传过去了。形参不是在Add函数内部创建的。即,a'为x,b'为y。vs2013下参数从右向左传。形参是实参的一份临时拷贝

返回

我们继续来看怎么返回。把ebp-8的值(算好的值)放到eax中。执行三次pop(弹出)指令,将原先压进去的三个寄存器弹出。把ebp赋值给esp。再pop(弹出)把结果弹到ebp中(取栈顶)。ebp就回到了原先main的栈底。

C语言--探索函数栈帧的创建与销毁_第31张图片

 ​​​​​​​C语言--探索函数栈帧的创建与销毁_第32张图片

 此时我们应该从call指令的下一条指令开始执行,ret指令,就是弹出栈顶(call指令的下一条指令的地址)然后跳到那去。

 exp跳到+8的位置,即将x,y还给了操作系统(销毁)。将eax的值放到ebp-20h处。

C语言--探索函数栈帧的创建与销毁_第33张图片

 

你可能感兴趣的:(c语言,c语言)