浅谈函数栈帧

简介: 可以直接说他就是一个空间,再准确点是存储空间。

目录

什么叫栈帧

该明白的一点知识

讲例

调试

结束


很抱歉我用的是vs19,这个笔记本不可以装两个vs,性能不够,不是什么好电脑,但vs19只有调用main函数的那个“东西”看不到,其他的还是可以看到很多的,在开辟空间上优化很多。废话不多说直接上

错误的地方本文有的地方ebp写成edp 由于修改地方太多就不改了,基本是写到一半才发现哎


什么叫栈帧

可以直接说他就是一个空间,再准确点是存储空间。

该明白的一点知识

1.ebp(栈底指针),esp(栈顶指针)这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的。

2.从高地址向低地址移动

3.压栈push :esp上移也就是朝低地址移动

出栈pop:也叫弹出,栈顶元素弹出,esp下移

4.每一个函数调用,都要在栈区创建一个空间


讲例

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


调试

我们首先进入调试,然后进入反汇编,只有底层才能讲的透彻。打开调用堆栈窗口。我们粗略调一遍会发现在调用堆栈里面我们看到了main函数,说明什么,有人在调用它,vs19我没看到,所以借vs13的来用一下

image.png

image.png

我们非常模糊的看到main函数被__tmainCRTStartup()调用,而这个函数还被mainCRTStartup调用

image.png

push ebp 就是说push压栈 把ebp压到esp顶部导致esp上移

mov ebp,esp 是把esp的地址交给edp

这两步实现了esp,edp都指向了同一个地址

 

image.png

sub esp,0E4h sub是把esp减去0E4h导致esp上移也就是开辟了空间

image.png

3push 依次压栈edx,esi,edi

image.png

lea (load effective address)加载有效地址

word是两个字节,dword(double word)双字四字节

上面的意思就是从edi开始向下的9个 字节全都初始化成eax的内容CCCC...

image.png

(这里当时忘了监测,重新调试了,所以地址都变了)但基本现象没怎么变。

到了这里也就意味着main函数栈帧的开辟 也就准备完了

image.png

mov 把0Ah(也就是10)放到ebp-8的位置上,而ebp-8实际上就是为a开辟一个空间

image.png

至于为什么a,b,c为什么不是紧密排的,这是编译器的原因,是为了防止数据紧密容易出错image.png mov 把ebp-20给eax,上面可以看到那是b的空间。

然后把eax压栈一下。

把a给ecx再把ecx压栈一下。

然后call我愿称他是奇计,我们可以非常清楚的看到a'上面的就是call指令的下一条指令的地址。他这一步是 在调用函数的同时把下一条指令的地址给压上去,这是回来的标记

image.pngimage.pngimage.png

这时才是真正的来到我们的Add函数上面和我们讲main函数栈帧一样

image.png

参数是从右向左压栈的,从上面我们也可以清楚的看到形参不是在Add函数内部创建的,而是回来找我们传参的空间,也直接的证明了形参是实参的临时拷贝这句话 image.png

请注意大风大浪过去了,千万不要在阴沟里面翻船

这里就是返回的变量明明销毁了,那如何把值给带回来的。把ebp-8里面的值也就是sum 放到eax里面,这里的eax可是寄存器啊,寄存器是不会因为程序退出就销毁的,相当于拿一个全局的寄存器把他保存起来,等我们到main函数里面再把它拿出来

image.png

pop弹出 把栈顶的元素取出放到edi里面去,依次pop三次,esp就加4加4往下走。当我们函数调用完了那这个空间就没必要存在了,所以把ebp的地址给esp

image.png 当esp指到这的时候pop一下把栈顶的元素弹出来,他里面放的是main函数的栈底指针,把结果弹到ebp里面去就可以瞬间到main栈底了

ret这条指令就是栈顶弹出call下一条指令地址然后跳过去,回来后就到了call下一条指令地方

image.png

 

这个add 就是把形参的空间还给操作系统

image.png

然后把eax的值给ebp-32空间就是c空间


结束

到了这里也就结束啦,只是非常浅显的讲解了一点,肯定还有好多地方没有讲到,寄存器这些就更底层了,也不是我们函数栈帧讲的主要内容,有机会以后再写寄存器的文章吧。

目录
相关文章
|
6月前
|
编译器
函数栈帧的创建和销毁
函数栈帧的创建和销毁
32 0
|
7月前
|
存储 程序员 编译器
深入理解函数调用--函数栈帧
深入理解函数调用--函数栈帧
|
7月前
|
存储 编译器 容器
函数栈帧的创建和销毁讲解
函数栈帧的创建和销毁讲解
46 0
|
7月前
|
编译器 容器
关于函数栈帧的创建和销毁
关于函数栈帧的创建和销毁
|
存储
函数栈帧的创建和销毁(下)
函数栈帧的创建和销毁(下)
54 0
|
7月前
|
容器
函数栈帧的创建和销毁介绍
函数栈帧的创建和销毁介绍
42 0
|
存储 缓存 编译器
函数栈帧的创建与销毁
函数栈帧的创建与销毁
41 0
|
编译器 C语言 容器
函数栈帧的创建和销毁(一)
函数栈帧的创建和销毁
118 1
|
存储 机器学习/深度学习 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
726 0
|
存储 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
118 0