函数栈帧的创建与销毁,带你了解代码底层原理
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++语言底层:函数调用过程之函数栈帧的创建和销毁(上)