注:结合上方学习链接阅读本文
下面深入讨论几个很基础的问题:
目录
1.在函数里面局部变量是怎么创建的?(局部变量的创建方式)
首先要为这次函数调用创建函数栈帧 有了这个函数栈帧 在函数栈帧里面找到一些空间把局部变量放入
2.为什么局部变量不初始化的时候值是随机值呢?
通过调试 反汇编可以观察到,这个随机值是开辟函数栈帧的时候放入的(CCCCCCCC),如果把局部变量初始化了,其实就是把随机值覆盖了
3.函数是怎么传参的?以及他的使用 传参的顺序是怎样的?
形式参数并不是在Add函数(见文首学习链接中Add函数)内部创建的,即不是在Add函数的函数栈帧里创建的,而是在还没有调用该函数的时候 通过push指令把参数按照从右向左的顺序压栈。当我们真正进入Add函数的时候 其实是在Add函数的函数栈帧里面 通过指针的偏移量找到了形参
下图是在主函数中的指令
先在主函数中为变量分配空间
然后压栈
然后call指令调用函数 同时把 call指令的下一条指令的地址(00461E7A) 压栈
4.形参和实参是什么关系?
形参是在压栈的时候开辟的空间,他和我们的实参只是值上是相同的,但空间是独立的,所以形参是实参的一份临时拷贝,改变形参不会影响实参
形参并没有在Add函数的函数栈帧里边,是在main函数和Add函数这两个函数的函数栈帧之间开辟的空间,或者也可以认为这块空间是在main函数的栈帧里边
5.函数调用结束后怎么返回的?
我们在调用Add函数之前:
1.就已经把 call指令的下一条指令的地址 压栈了,
2.把 上一个函数(main函数)的 栈帧的ebp 压栈压进去了(如下图)
上面这两步非常重要 当我们函数调用完要返回的时候:
1.pop指令将esp所指地址处的值(main函数的栈帧的ebp)赋给ebp,这样就回到了main函数的栈帧空间
2.ret指令终止当前函数的执行,因为我们之前记住了call指令下一条指令的地址,往回返的时候就可以跳转到该地址,让我们函数调用之后可以返回,进而继续执行main函数
返回值是通过寄存器的方式带回来的,寄存器不会被销毁:
Add函数的返回值先放到寄存器(eax)里面去
回到main函数之后mov指令再把这个值放到局部变量c([ebp-20h])里面去