函数过程调用
这一章中先以如下代码来讨论一下函数调用的大致过程:
解释:
首先明确一点:
在函数执行的过程中,变量,参数,地址等都是放到栈中:
而且需要注意的是:
栈的栈底是在高地址,每一次入栈,栈指针寄存器ESP都会 -=
Call指令 会将Call指令的下一条指令的地址入栈,这条保存的数据被称为返回地址
Ret指令 会将目前ESP所指向的地址,其保存的数据放回到EIP(程序计数器)中
然后EIP在不断地取指,执行
注意每一次执行Ret时,都保证ESP目前指向的是当时入栈的返回地址(如果不是的话当然会出错了)
这样保证了当函数调用返回时,可以延续上一个函数中继续执行
返回值 (即函数return 后面跟着的变量)都是保存到EAX中的
《函数调用的机器级代码表示》
我们以上面这个代码为例,继续深入讲述函数调用过程
上述代码是 函数P 调用 函数 caller ,函数 caller再调用 add
如下在上面一段函数的汇编代码:
这个过程中 栈中如下:
解释:
首先 pushl %ebp 将EBP中的数据保存到栈中
movl %esp,%ebp 将ESP(栈指针)保存到EBP ,
这个过程相当于让两个指针 ebp 和 esp 指向栈中同一个地址
然后subl $24 ,%esp 即 %esp - 24
这个操作是说明有数据要入栈了,栈指针要移动
然后形成了:
这个时候EBP 和 ESP 分别形成了 一个函数的栈底和栈顶的感觉
这一段汇编代码都是在栈底和栈顶之间 保存数据
这个时候 调用 call指令了 其将下一条指令 movl %eax,-4(%ebp) 的地址入栈
形成返回地址
然后转移到add函数的代码段处执行(这个过程还有保存现场的操作,这里就不说了)
然后add函数代码段的汇编代码开头和结尾其实都与其他函数差不多
都是:
然后接着是形成 add函数 的栈底和栈顶的过程(即通过移动EBP和ESP)
注意这里ESP栈底指向的内容是上一个函数中EBP的内容
即上一个函数中栈底的地址
然后执行。。。执行。。。
执行到add函数要结束了
将 目前EBP中的值 送到 ESP中,即让当前函数(add)的栈顶返回到栈底
这个函数中全部保存的数据随着 栈顶的返回而没用了
然后popl %ebp 即将当前栈顶中的内容 压入到 EBP中
即EBP现在保存了 上一个函数(caller)中栈底的内容
即EBP现在又回到 caller函数的栈底了
然后 因为pop了 ESP中的内容(即栈指针)肯定要+
则回到了
然后执行ret指令,取出 当前栈顶中的内容送到EIP中
即取出返回地址中的内容(即caller函数中的地址)
这个时候已经彻底回到 caller函数中继续执行了
《函数过程调用案例》
思考一下变量在我们程序的虚拟内存中是如何存放的
应该如图存放,再想一想如果一个数组超过了其原先定义的大小
(即我们常常说的:你数组下标越界了)
这个时候在我们程序的虚拟内存中是如何变换的呢?
如图所示:
既然我们定义了a这个数组,那么在响应的虚拟地址中就会有其存放位置
从汇编代码和图中可以知道:
a[0]被放到了ESP(即栈顶)的位置处
然后
这条汇编指令是将a[i]=1073.....24这个操作的
在汇编中如何知道a[i]的位置?
当然是以a[0]为基址+i*4(4是因为一个int4个字节 )
现在你可以想一下,以我们这个案例为列:
如果i超过了2,是不是a[i]会被保存到
这个上面来?
对的,这样会覆盖原先的已经保存好的值
导致十分奇怪的情况发生
本文作者:次林梦叶的小屋
本文链接:https://www.cnblogs.com/cilinmengye/p/17347400.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2022-04-23 acwing----线段树