一 函数栈帧的创建
我们首先来一步步解析上一篇文章的汇编代码
00BE1820 push ebp //把ebp寄存器中的值进行压栈,此时的ebp中存放的是 invoke_main函数栈帧的ebp,esp-4 00BE1821 mov ebp,esp //move指令会把esp的值存放到ebp中,相当于产生了main函数的 ebp,这个值就是invoke_main函数栈帧的esp 00BE1823 sub esp,0E4h //sub会让esp中的地址减去一个16进制数字0xe4,产生新的 esp,此时的esp是main函数栈帧的esp,此时结合上一条指令的ebp和当前的esp,ebp和esp之间维护了一 个块栈空间,这块栈空间就是为main函数开辟的,就是main函数的栈帧空间,这一段空间中将存储main函数 中的局部变量,临时数据已经调试信息等。 00BE1829 push ebx //将寄存器ebx的值压栈,esp-4 00BE182A push esi //将寄存器esi的值压栈,esp-4 00BE182B push edi //将寄存器edi的值压栈,esp-4 //上面3条指令保存了3个寄存器的值在栈区,这3个寄存器的在函数随后执行中可能会被修改,所以先保存寄 存器原来的值,以便在退出函数时恢复。
上面的这段代码最后4句,等价于下面的伪代码:
edi = ebp-0x24; ecx = 9; eax = 0xCCCCCCCC; for(; ecx = 0; --ecx,edi+=4) { *(int*)edi = eax; }
二 小知识:烫烫烫烫
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { char arr[20]; printf("%s", arr); return 0; }
下面这段代码之所以会发出来烫烫烫烫的原因是因为函数初始化的值为occcccccch
oxcccc的文本就是“烫”
三 函数中临时变量的创建
四 Add()函数的调用
函数调用过程
call 指令是要执行函数调用逻辑的,在执行call指令之前先会把call指令的下一条指令的地址进行压栈
操作,这个操作是为了解决当函数调用结束后要回到call指令的下一条指令的地方,继续往后执行。
函数栈帧的创建和压栈和main()函数是大差不差的
通过观察x y的值我们可以更加深刻的理解 为什么函数的形参是实参的零食拷贝这句话
当最后计算出z的结果的时候
将z存放到寄存器当中 之后开始销毁add()的栈帧
五 函数栈帧的销毁
六 回答文章最初的问题
1 局部变量是如何创建的?
首先函数创建一个栈帧空间 初始化好之后空间之后再为局部变量分配好空间
2 为什么局部变量不初始化内容是随机的?
因为里面的值是我们随机放进去的 初始化后随机值就会被我们覆盖
3 函数调用时参数时如何传递的?传参的顺序是怎样的?
当调用函数之前 从右向左开始压栈
4 函数的形参和实参分别是怎样实例化的?
形参是实参的一份临时拷贝
形参在函数调用之后进行实例化
实参在函数中进行实例化
5 函数的返回值是如何带会的?
通过寄存器带回