内功修炼-深入了解函数栈帧

基础铺垫

函数栈帧的创建和销毁,在不同编译器,不同环境下有区别。

寄存器 eax ebx ecx edx   ebp  esp

要理解函数栈帧就必须理解 ebp esp 这两个寄存器中存放的是地址是用来维护函数栈帧的。

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

压栈(push)就是从栈区的顶上放一个元素进去,从顶上删除一个元素就叫出栈(pop)。

寄存器是独立于内存的,是在cpu上面的。

操作工具是vs2013,因为vs2013可以看到栈帧的详细过程。

局部变量是怎么创建的

再调试的时候,我们可以发现 main 函数是被下面这个函数调用的:
在这里插入图片描述
继续往下看,可以发现这个函数是被下面这个函数调用的。
在这里插入图片描述
所以我们就可以知道,在进入 main 函数的时候,编译器先调用这两个函数,献给这两个函数开辟栈区空间,然后再给 main 函数开辟空间。在调试的时候,右击转到反汇编,就可以查看栈区具体的开辟和回收过程了。
进入反汇编,查看具体的栈区开辟和回收过程
在这里插入图片描述
这里就是进入反汇编的页面,箭头指向 main 函数的开始。所以刚开始就会先给 _tmainCRTstartup 开辟一段栈空间,ebp 寄存器指向下面。esp 寄存器指向上面。

在这里插入图片描述
因为内存是由高地址向低地址蔓延的,所以会向低地址处使用。第一个执行的是 push ebp 就是压栈,把一个元素压在栈顶,然后让 esp 指向这个元素的上面

在这里插入图片描述
接下来的指令是 mov ebp,esp 就是把 ebp 的位置 移动到 esp 的位置
在这里插入图片描述
下一条指令是 sub 就是减的意思 esp,0E4h 就是把 esp 往下移动 0E4h 个位置。0E4h 对应的10进制是 228。
在这里插入图片描述
说白了就是把 esp 向低地址移动一段空间,这段空间给 main 函数开辟

在这里插入图片描述

然后继续在上面压三个元素
在这里插入图片描述

在这里插入图片描述

接下来是 lea 就是 load effective address 加载有效地址的意思,就是把后面这个的有效地址放到 edi 里面

在这里插入图片描述
选择勾选符号名之后,就是 ebp - 0E4h 。在前面的 esp 就减过了 0E4h ,上面说的 0E4h 就是 main 函数的栈帧,所以这里就是到了 main 函数的上面
在这里插入图片描述
在这里插入图片描述
接下来是 ecx,39h ,eax,0CCCCCCCCh ,dword ptr es:[edi] 。一个 word 是两个字节,dword 就是双,所以这里是4个字节。就是把 edi 往下的 39h 个字节改为 eax 的内容,就是全部改为 0CCCCCCCCh ,就是从 ebp 到 ebp-0E4h 全部改为 0CCCCCCCCh

在这里插入图片描述
通过监视找到 ebp 和 ebp-0E4h 的地址,方便观察。

在这里插入图片描述
然后执行完 dword ptr es:[edi] 的结果就是这样

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从 ebp 和 ebp-0E4h 全部被初始化 cccccccc 这样 main 函数的空间就全被初始化成了这样,也就是为什么有的时候我们打印字符串的时候会出现“烫烫烫烫”了。每一行的8个 c 就表示 4 个字节。所以每一行就是 ebp-4

上面这些就是为 main 函数执行的代码,main 函数里面的有效代码还没有执行

接下来就是执行 int a = 10; 这行代码了,就是把 0Ah 放到 ebp-8 里面,所以这个空间就是 a 。
在这里插入图片描述
所以这个空间就是 a 的空间,就是10。在这里插入图片描述
因为创建变量的时候,对这段空间赋值了,如果补赋值的话,就是 cccccccc 。
接下来是创建 b 这里是16进制,所以14就表示20
在这里插入图片描述
发现 b 和 a 之间隔了8个字节
在这里插入图片描述
然后再把 c 放进去,发现依然是隔了8个字节
在这里插入图片描述
然后可以知道在 main 的函数栈帧里面局部变量的存放方式
在这里插入图片描述

下来就是函数的传参,mov 就是移动,把 ebp-14h 移动到 eax 里面,看前面可以发现 ebp-14h 就是 b ,所以就是把 20 放到 eax 里面
在这里插入图片描述
然后 push 压栈,压的是 eax ,就是继续往栈顶上面压一个 eax ,eax 里面放的就是 20
在这里插入图片描述
在这里插入图片描述
eax 结束之后,接下来就是把 ebp-8 放到 ecx 里面,ebp-8 就是前面的 a 。然后再把 ebp-8 压到 ecx 里面,ebp-8 就是 10 。
在这里插入图片描述

在这里插入图片描述
因为马上就要调用函数了,所以这两个动作就是在传参,等等就会用到,接下来是 call 指令,call 指令就是调用函数的意思
在这里插入图片描述
运行之后发现 call 后面又压了一个地址 0028518a ,就是又给 0028518a 压了一个空间。这个地址就是 call 函数接下来的地址。
在这里插入图片描述
在这里插入图片描述

因为调用完 call 函数之后还要回来,回来就到了下面这个这个地址,就能往后执行了。
然后再下一步们就到了函数里面,这次就是到了函数里面。

在这里插入图片描述
发现前面一部分和 main 函数的一部分一样,说明是在为 Add 函数做准备,执行完的结果就是这样。ebp-8 就是 z

在这里插入图片描述
接下来的就是相加操作了
在这里插入图片描述
把 ebp+8 就是前面的 a 就是 10 放到 eax ,然后 add 往 eax 加上 ebp+0Ch 就变成了 30 。然后再把 eax 的值放到 ebp-8 里面去,ebp-8 就是创建的 z 。

因为 z 在加的过程当中,是从前面拷贝的值里面相加,所以传的形参,也就是我们常说的值传递,也就是值拷贝。传参的时候,函数还没创建的时候,就把参数传过去了,使用的时候,就是从之前压的栈区里面找回来这两个形参。

接下来是返回 z ,这里是把 ebp-8 的值放到 eax 里面,因为 eax 是寄存器,不会被销毁。

在这里插入图片描述
放到 z 里面之后,就是接下来的 pop 三句话,pop 就是弹出的意思,三次 pop 就是把 edi esi ebx 从栈顶弹出去
在这里插入图片描述
弹出去之后的栈区就是这样
在这里插入图片描述
接下来是 mov esp,ebp 就是把 ebp 的值给 esp 就使得 esp 也回到了 ebp 的位置
在这里插入图片描述
esp 就变成了这样

在这里插入图片描述
然后又是一次 pop 就把 main 栈帧的栈顶弹出去了,esp 就指向了下面的这个,就是 call 指令存放的地址。就回到了 main 函数的空间
在这里插入图片描述
在这里插入图片描述
就指向了 call 下来的地址,回来的时候也就把 call 指令存的那块空间弹出去了
在这里插入图片描述
下来是 add esp,8 就是给 esp+8 就跳过了 a ,b 形参的空间,就把这两个形参的空间释放了 。

在这里插入图片描述
就是下面这样
在这里插入图片描述
下面就是 mov [ebp-20h],eax 就是把 eax 的值放到 [ebp-20h] 里面,[ebp-20h] 就是前面的 c ,eax 就是出 Add 函数的时候,放到 eax 里面的 z 又放到 c 里面了。
在这里插入图片描述
上面这些就是 Add 函数的创建和销毁的过程

Add 函数的创建和销毁和 main 函数的创建和销毁一样。

文章很长 但是只要理解对以后的学习都是很大的帮助。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lockey-s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值