学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】

=========================================================================

相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

=========================================================================

接上期:学C的第十天(继续深入学习函数:函数的嵌套调用,链式访问,声明和定义;函数递归:了解递归和其两个条件;练习:1.接收并打印整形值、2.时变不用临量求字符串长度、3.求n的阶乘、4.求第n个斐波那契数)_高高的胖子的博客-CSDN博客

=========================================================================

函数栈帧的创建和销毁

  • 越高级的编译器,越不容易学习和观察该过程

  • 同时在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,具体细节取决于编译器的实现

1.寄存器:ebpesp(和函数栈帧有关)

esp栈顶指针        ;        ebp栈低指针


  • 寄存器集成在CPU上

  •  ebpesp 这两个寄存器中存放的是地址

  • 这两个地址是用来维护函数栈帧

1. 每一次函数调用,都要在栈区创建一个空间

2. 正在调用哪个函数,esp 和 ebp 就在维护哪个函数的函数栈帧

3. esp 和 ebp 之间的空间就是系统为这次函数所调用的空间,叫这次函数的函数栈帧

4. 栈区的使用习惯使用地址,使用地址

5. 空间消耗时,从高地址向低地址消耗

6. 再开辟新空间时,使用的空间是上面的空间往上使用

7. 像栈一样,放数据在顶上(栈顶)放数据

测试代码:

#include 

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;
}


VS2013中,main函数也是被其它函数调用的

mainCRTStartup        -->        __tmainCRTStartup        -->        main函数 

        (调用)                                           (调用)

 (所以实际开辟的空间为:)

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第1张图片

 (查看汇编代码:)

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第2张图片


(1).push压栈):给栈顶放一个元素

        [补充:pop出栈) -->   从栈顶删除一个元素]

(压栈前:)

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第3张图片

(压栈后:esp会往上移,移到压的元素上方)


(2).mov把后面的值赋给前面,把esp的值赋给ebp):

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第4张图片


(3).sub让esp减去一个十六进制数):


(4).连续push三次:

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第5张图片


(5).leaload effective address -- 加载有效地址,把一个有效地址加载到edi中):


(6).两次mov后,rep stos

之前出现过的“烫烫烫”乱码的原因:

变量未初始化,变量里面的数据是“cc cc cc cc”,这些“cc cc cc cc”在使用后会产生随机值,即"烫烫烫",而初始化就会将这些随机值覆盖


(7).产生局部变量:int a = 10; (mov)


(8).产生局部变量:int b = 20; (mov)


 (9).产生局部变量:int c = 0; (mov)

========================================================================= 

(总结上面步骤)局部变量(上面的a、b、c)的创建过程:

  • 为这次函数调用创建函数栈帧   -- (1)~(6)

  • 在函数栈帧中找到空间把局部变量放进去   --(7)~(9)

=========================================================================

(10).调用函数:传参(mov


(11).调用函数:传参(push


(12).调用函数:传参(mov


(13).调用函数:传参(push


(14).call:调用函数(进入Add()函数)


(15).进入Add()函数后:

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第6张图片

当前开辟的空间情况:)

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第7张图片


(16).Add()函数push


(17).Add()函数mov


(18).Add()函数sub


(19).Add()函数连续三次push


(20).Add()函数lea(加载有效地址) --> mov  --> mov --> rep stos


(21).Add()函数中产生局部变量:int z = 0; (mov)


(22).Add()函数中进行计算:z = x + y

 形参的产生和使用:

  • 形参是对实参的临时拷贝:形参是调用的main函数中对变量的拷贝,即下图 ecxeax所以改变形参,改变的也只是 ecxeax ,并不会改变main函数中的实参

  • 压栈时:先压的b’,所以在a‘下面,所以传参是先传的形参y再传的形参x

  • 形参的使用:通过指针的偏移量找到形参

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁 讲解】_第8张图片


(23).Add()函数计算后进行返回return z


(24).Add()函数调用完后销毁空间返回main函数pop -- 出栈(弹出栈顶元素)


(25).Add()函数调用完后销毁空间返回main函数:ret -- call函数调用完后返回main函数call的下一条指令(之前留的地址会出栈)


(26).main函数:销毁形参


(27).main函数:使用Add函数的返回值


(28).最后main函数的销毁和Add()函数的销毁类似

你可能感兴趣的:(CCC全是C,c语言,c++,c#)