0


函数栈帧的创建和销毁(详细版)

一、需要的知识储备(准备工作)

1、常用寄存器类型

<1>、 sp/esp/rsp 栈顶指针

<2>、 bp/ebp/rbp 栈底指针

       这两个指针的作用是维护栈帧的范围

<3>、 eax 累加寄存器

<4>、 ebx 基地址寄存器,在内存寻地址时存放基地址

<5>、 ecx 计数寄存器

<6>、 edx 多功能寄存器,总是被用来存放整数除法产生的余数

2、部分汇编指令的认识

<1>、push指令:称为压栈,从栈顶增加一个元素。具体操作为:它首先减少esp的值,即esp向上移动,再将操作数复制到esp所指向的地址,在32位计算机下,esp每次减少4个字节

<2>、mov指令:用于将一个数据从源地址传送到目标地址,原操作地址的内容不变

      注解:此处即为esp的值给ebp,就是ebp移动到esp的位置

<3>、pop 指令:称为出栈,和压栈相对应的从栈顶删除一个元素,同时esp++

<4>、call 指令:将该指令的下一条指令的地址压入栈中,并跳转(jump)到子函数中

<5>、sub 指令:减操作指令,从寄存器减去数值,然后将结果保存到寄存器中

<6>、add 指令:加操作指令,从寄存器加上数值,然后将结果保存到寄存器中

<7>、lea 指令:为“load effective address”的缩写,可以将地址赋给目标寄存器

<8>、rep 指令:重复指令

     注解:即从edi开始39h(十六进制)个空间全部令为eax的内容,即0cccccccch,(相当                      于初始化)每次操作四个字节(每个word是两个字节,dword为四个字节)

3、栈的存储方式以及main函数的栈帧

1>、栈的存储方式

在栈中,上位是低地址,下位是高地址,在函数调用时会优先分配高地址空间,然后再分配低地址空间。

2>、main()函数的栈帧

在C语言中main函数也是被调用的,如下图中:

<1>、代码先给mainCRTstartup分配空间,然后进入mainCRTstartup中调用__tnainCRTstartup

<2>、分配空间给__tnainCRTstartup,然后进入__tnainCRTstartup,再调用main

<3>、分配空间给main,然后才进入main函数中

从vs上演示:

注释:vs2019及以上版本无法演示调用main函数的函数

用vs2013如下图所示:

用F10调试,查看调用堆栈,跳出main函数后,发现main函数之外还有两个被调用的

在此处:即mainCRTstartup调用__tnainCRTstartup

          进入mainCRTstartup函数后

          __tnainCRTstartup调用main函数

二、函数栈帧的创建和销毁

代码如下(示例):

#define _CRT_SECURE_NO_WARNINGS
#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;
}

1、关于main()函数的操作

1>、为main()函数开辟栈帧

2>、在main()函数中创建变量

2、关于Add()函数操作

1>、调用Add()时的准备

  ** **

2>、给Add()函数开辟栈帧

3>、在Add()函数中创建变量并计算

4>、Add()栈帧的销毁

3、返回main()函数

总结

看到这里,想必之前存在的很多疑问已经迎刃而解。

比如:

1、局部变量是怎么创建的?

2、为什么局部变量的值是随机值?

3、函数是怎么传参的?传参顺序是怎样的?

4、形参和实参是什么关系?

5、函数调用是怎么做的?

6、函数调用是结束后怎么返回的?


本文转载自: https://blog.csdn.net/m0_63979882/article/details/125631766
版权归原作者 学Java的冬瓜 所有, 如有侵权,请联系我们删除。

“函数栈帧的创建和销毁(详细版)”的评论:

还没有评论