函数栈帧的创建与销毁,带你了解代码底层原理

Posted Ja_king_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数栈帧的创建与销毁,带你了解代码底层原理相关的知识,希望对你有一定的参考价值。

在学习C语言时,我们难免有许多疑问
例如:
(1)局部变量是怎么创建的?
(2)为什么局部变量的值是随机的?
(3)函数是怎么传参的?传参的顺序如何?
(4)形参和实参是什么关系?
(5)函数调用是怎么做的?
(6)函数调用结束后是怎么返回的?

这些疑问其实都和函数栈帧的创建与销毁有关

寄存器

eax,ebx,ecx,edx,ebp,esp等都属于寄存器

ebp与esp 这两个寄存器放的是地址,这两个地址用来维护函数栈帧的,每一次函数调用都要在栈区上开辟一块空间

在VS2013中main函数被__tmainCRTStartup所调用

以下面代码做为研究:

#include<stdio.h>
int Add(int x, int y) {
	return x + y;
}
int main() {
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d", c);
	return 0;
}

按f10进入调试,右键进入反汇编

如下图:(如果显示的是符号,可以右键将符号去掉)


接着我们开始逐条执行,此时ebp和esp分别存放两个地址

这两个寄存器存的地址便是__tmainCRTStartup函数所压的栈

随着代码的执行,esp会指向ebp所存地址

此时esp进行减法

地址变化如下:

相当于为main函数预开辟一段空间,执行如下图所示:


接下来的三步push相当于压了三次栈


接下来几步相当于将main函数中全部初始化为CCCCCCCC

然后接着执行

这步的意思是将a放在ebp-8的位置


此处即是为a开辟的空间,所以当a未初始化时,a放的便是CCCCCCCC,所以为随机值

创建的a和b在内存中便如上图一样存放

接着,代码调用Add函数

此时,我们可以观察到,用eax来存ebp-14h以及用ecx来存ebp-8的地址,而ebp-14h和ebp-8分别是b和a的地址

调用之后压栈如上图

执行到call这步后,按f11进入所调用函数内部

函数内部如下

然后我们可以观察到,调用的Add函数前半部分和main函数的初始化一样,为Add函数的准备工作,而第一条语句的push相当于将原来压在main函数的ebp压在Add上

接下来几条语句便是Add函数的栈帧

执行完前面语句后,x,y呢?

这两条语句我们看到,在需要x和y时,我们找到了ebp+8和ebp+0Ch

而此时ebp+8和ebp+0Ch恰好表示ecx和eax,即存放的a和b,所以形参不是在Add函数内部创建的,而是在寄存器中找的

函数的传参是在函数创建之前就已经压栈了,所以我们说形参是实参的一份临时拷贝,所以改变形参并不会影响实参

当函数执行到此处时,弹出edi,esi和ebx,然后将ebp地址赋给esp,即销毁掉Add函数

热门文章:
AI三子棋,极大极小值算法
扫雷游戏
DP动态规划

以上是关于函数栈帧的创建与销毁,带你了解代码底层原理的主要内容,如果未能解决你的问题,请参考以下文章

图解C/C++底层:函数栈帧的创建和销毁(下篇)

图解C/C++语言底层:函数调用过程之函数栈帧的创建和销毁(上)

图解C/C++语言底层:函数调用过程之函数栈帧的创建和销毁(上)

函数栈帧的创建与销毁

C语言进阶 顶级神功! 函数栈帧的创建和销毁

超详细 函数栈帧(利用反汇编窥探底层原理)+ 建议收藏