- 寄存器(集成到CPU上的): eax、ebx、ecx、edx
函数栈帧:ebp、esp这两个寄存器中存放的是地址,这两个地址用来维护函数栈帧。
- 每个函数调用都需要在栈区创建一个空间
- 每个函数调用都需要栈帧
- 栈空间使用从高地址到低地址 就像栈,先放底,再放顶
- 一开始,因为main函数也是被别的函数调用的,叫__tmainCRTStartup,esp和ebp分别指向该栈帧的顶地址和底地址,当调用main函数,用esp上一个内存存ebp,并且ebp指向它,然后esp减0E4h(因为低地址未被用,因为是从高到低使用的, 0E4h显然是main占的空间大小)。总之,调用main的时候,esp和ebp一上(低地址)一下(高地址)地维护main()函数的栈帧。
- 之后esp上面(在栈顶上放)又有了ebx、esi、edi,即入栈,所以esp也在依次上挪,之后lea=load,之后,edi向下的一片范围为内,所有main内存中的值,全赋值成CCCCC的内容,图中一行是4个字节,之后就能运行你的main中的代码了,比如此时有int a = 10; 就给低内存最下面赋值10,如下图一行,即一个字节。而如果初始化只定义了 int a;直接输出a,就会输出烫烫烫烫烫烫烫烫,其实这是CCCC值对应的内容
同时,下图中内存中,最下面地中中给了变量值,赋值成了10。 8*4 = 32位, 32/8 = 4 字节正好是一个int类型,此后再如果有int b = 20; 会给它间隔两个字节的地址赋值,这种方法是vs2013,而其它编译器可能紧挨着放。
-
此时已经知道,程序运行先创建栈帧,再执行内部语句。push:利用eax,ecx(传参)等等寄存器完成操作,这些是不断在esp上面做的,esp也在不断上挪。后面遇到call指令(因为现在开始用函数了),(先记下call地址,44B),call指令把下一条指令的地址压到栈顶,因为后面是要开始进入一个函数,比如要跳到add里面还要出来,需要回来就利用call压到栈顶的地址,之后来到add函数,和之前main中变量运行一样,也是准备地址,放值,esp一直也在挪,在add内部,push,先压ebp的地址,然后move,esp,然后执行做内部语句,次开辟空间为add()函数,里面还是一大坨CCC,然后复制完,传入的参数值,会存在add的低地址部分,执行函数之前,比如遇到add(3, 4),这个时候,存3,4到地址中,等在esp、ebp开辟了add空间后,用到这两个的时候,又返回去找3、4的地址中的值,所以在函数还没调用的时候,实参的值,已经存到了栈里面,所以形参是实参的临时拷贝。 返回过程:算好的值比如z变量带的值存eax中,因为一会销毁z值,等回到主函数,把寄存器eax值拿出来,然后是一直在pop(),然后顺便销毁空间,回收的时候:move,把ebp赋给esp,b在下面,所以相当于esp向下移动了(低地址到高地址,释放了空间),把之前存到的地址都用起来,因为在调用函数的时候,会存各个函数的ebp值,(因为之前不断在存栈底的值)main函数的,然后esp、ebp(两个寄存器)不断往回挪,然后回到main函数继续往下走,一个指令pop就能让他们回去,当遇到ret指令时,弹出之前call指令存的call的下一条指令地址,ret让回到了主函数call的下一条语句。此时形参用完了,因为add值都返回了,所以把之前参数的值从栈弹出,esp往下挪(即低地址到高地址),esp往下走,相当于释放空间,总之函数返回值在寄存器比如eax中,然后用就拿。 -
如果再碰到其它函数,再创建栈帧。 -
栈帧的销毁过程:1.18
局部变量怎么创建的?
在栈帧中分配空间
为什么局部变量不初始化是随机值:
因为之前在开辟空间给了CCCCC ,初始化会覆盖
函数是如何传参的?
当调用函数,还没调用的时候,已经把push压栈进去了参数,真的进入了函数的时候,通过指针偏移量找到在栈内的形参值,所以形参值先存,后才开辟函数空间。
形参和实参区别???
- 形参在栈内有自己的地址,值是实参的临时拷贝,只是值相同,改变形参不会影响形参
函数调用的结果是怎么传回来的?
call指令下一条指定的地址记住了,走完函数,弹出上一条ebp地址是上一个函数的栈底地址,然后把函数值存在一个寄存器中,销毁之前函数空间,然后利用之前calll指令存到的下一条地址指令地址。
- 开辟空间大小、不够之类的,问题,可以说:这取决于编译器。编译器很智能
|