main函数栈帧的创建
先在栈帧里面为main函数开辟出一块空间,且这块空间由两个寄存器esp和ebp来进行维护,当在调用另一个函数时这两个寄存器就去维护为这个函数栈帧,也就是当调用哪个函数时这两个寄存器就去维护哪个函数的栈帧。每个函数在调用时都会在栈区创建一个空间。我们成ebp为栈底指针,esp为栈顶指针。
压栈的时候是从栈顶压入数据的。
存到的地方是低地址
我们以一个程序来进行详细说明
#include <stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%d\n", c);
return 0;
}
实际上main函数也是被别人调用的。那么main函数是怎么进行栈帧的创建的呢?
这就是main函数栈帧的创建,创建结果为
这是在调用主函数时空间开辟后进行的准备工作,其它普通函数也是这样。
esp会自动调整始终指向栈顶
接下来我们再对内部的变量以及内部加粗样式调用函数的创建和销毁进一步说明。
为什么局部变量的值是随机值
当你不对变量初始化赋值时,默认就是CCCCCCCC因为在你为函数开辟战真的时候就已经默认为这个值了,这也就是为什么会出现烫烫烫烫的原因
当你初始化时
此时将a赋值为10
函数是怎么传参的?传参的顺序是什么?
在call指令调用调用函数之前,先把参数分别赋给了eax和ecx并压入了栈中。
当执行call指令时调用函数的同时还进行了另一部操作:
这一步操作是把call指令的下一条指令的地址压入了栈中,因为当调用函数结束之后还是要返回执行下一条指令的,这里就是为了找到call指令的下一条指令,因此提前保存起来了
当进入被调函数之后,对参数进行操作时就是对我们在执行call指令值前压入的两个值进行操作,这两个值是参数的拷贝,所以他们的改变并不会影响实参。由上面我们也可以看出形参压栈的顺序是从右向左入栈。
函数调用结束是怎么返回的
因为在函数调用结束时,被调函数中的局部变量就会被销毁,所以编译器会先把返回值放到寄存器当中,然后返回到刚刚保存的下一条指令那里,然后再将形参进行销毁,最后回到刚才调用函数的地方。
这里对形参进行销毁的指令是
就是让栈顶指针向高地址移动8,即esp+8