英文广告网站模板免费下载,有限公司和公司的区别,标签管理wordpress,青岛建设集团有限公司本章重点 一、何为函数栈帧 二、函数栈帧特性 - 同栈 - 后进先出 三、认识内存空间布局图 四、认识相关寄存器 五、认识相关汇编命令 六、测试代码#xff1a; 七、函数栈帧全过程 要解决的问题 局部变量是怎么创建的#xff1f;为什么局部变量的值是随机值 七、函数栈帧全过程 要解决的问题 局部变量是怎么创建的为什么局部变量的值是随机值函数是怎么传参的传参的顺序是怎么样的形参和实参是什么关系函数调用是怎么做的函数调用结束后怎么返回的 一、何为函数栈帧 函数栈帧Function Stack Frame也被称为调用栈帧Call Stack Frame是在计算机程序中用于管理函数调用和执行的一种数据结构。每当函数被调用时都会创建一个新的函数栈帧用于存储该函数的局部变量、参数、返回地址和其他执行相关的信息。当函数执行完毕或返回时相应的函数栈帧将被销毁控制权回到上一个函数的栈帧中。 二、函数栈帧特性 - 同栈 - 后进先出 函数栈帧的管理遵循一种后进先出Last-In, First-OutLIFO的原则。这意味着最后调用的函数的栈帧会首先被执行然后在函数返回时销毁程序控制权回到上一个函数的栈帧中以此类推直到回到主程序的栈帧整个程序执行完毕。 三、认识内存空间布局图 栈Stack 栈是一种线性数据结构用于存储函数调用时的局部变量、函数参数以及函数调用的返回地址。栈内存是有限的以栈帧的方式分配和释放遵循后进先出Last-In, First-OutLIFO原则。栈内存的生命周期与函数调用关联当函数返回时其栈帧上的数据会被销毁。 堆Heap 堆是一块较大的、动态分配的内存区域用于存储程序运行时动态分配的数据如动态分配的对象和数组。堆内存的生命周期不受函数调用的影响需要手动分配和释放否则可能导致内存泄漏。在C中通常使用malloc()、calloc()和realloc()等函数来分配堆内存使用free()来释放堆内存。 全局变量区Global Variables 全局变量区存储程序中的全局变量这些变量在整个程序的生命周期内都可访问。全局变量在程序启动时被分配在程序结束时被释放。 常量区Constants 常量区存储常量数据如字符串文字和全局常量。这些数据在程序运行期间是只读的。 代码区Code 代码区存储程序的二进制代码包括所有函数的机器代码。代码区通常是只读的不允许修改程序代码。 四、认识相关寄存器 eax通用寄存器保留临时数据常用于返回值 ebx通用寄存器保留临时数据 ebp栈底寄存器 esp栈顶寄存器 eip指令寄存器保存当前指令的下一条指令的地址 注ebp和esp主要用于维护当前存在的这个函数栈帧。 问函数是被调用的那main函数呢它是谁调用的 在VS2013main函数也是被其他函数调用的该函数是_tmainCRTStartup而该函数又是mainCRTStartup函数调用的而mainCRTStartup函数则是由操作系统调用的。所以从编程者的角度来看main函数似乎是程序的入口点但实际上在特定的编程环境中它是由启动代码调用的。这些启动代码负责设置程序环境并确保main函数能够正确执行。 五、认识相关汇编命令 mov数据转移指令push数据入栈同时esp栈顶寄存器也要发生改变pop数据弹出至指定位置同时esp栈顶寄存器也要发生改变sub减法命令add加法命令call函数调用1. 压入返回地址 2. 转入目标函数jump通过修改eip转入目标函数进行调用ret恢复返回地址压入eip类似pop eip命令lea将表达式addr的值放入寄存器 六、测试代码
#includestdio.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.ebp寄存器push压栈同时esp栈顶寄存器地址值改变 - 变小 2.ebp寄存器数据转移到esp寄存器 3.0E4h的十进制是288sub减法指令此时开始创建main函数栈帧main函数此时空间大小就是ebp寄存器-esp寄存器的差 0E4h 4.ebx寄存器esi寄存器和edi寄存器push压栈同时esp栈顶寄存器地址值改变 - 变小 5. 将表达式ebp-24h的值放入edi寄存器 6.word一个字两个字节dword表示双字四个字节刚好表示一个整型变量的大小。move将表达式9移入到ecx寄存器中代表重复的次数move将表达式CCCCCCCCh移入eax寄存器中代表数值。rep stos就是重复执行stos指令stos指令将eax中的值拷贝到ES:EDI指向的地址所以这串指令就是在main函数中找到9个连续的4字节空间将其值设置为CCCCCCCCh。 7.局部变量abc开始创建并进行初始化 8.函数传参过程传参过程仍然是在main函数且传参的过程是从右到左先传y后传x。 9.call函数调用1. 压入返回地址 00682297方便函数调用后返回到main函数栈帧2. 转入目标Add函数 10.Add函数栈帧形成这个过程同mian函数栈帧形成的过程相同唯一的区别是在Add函数中找到3个连续的4字节空间将其值设置为CCCCCCCCh。 11.变量z创建并被初始化为0. 12.将地址为ebp8放到eax寄存器中然后在将ebp14h的值与eax寄存器之前存放的值相加放到eax寄存器中再将eax寄存器的值放到ebp-8处也就是z变量地址处。 13.将运算的结果放到寄存器中由于变量z是局部变量出了函数作用域就会被销毁为了能顺利带回返回值利用寄存器不会被销毁的特点将运算的结果放到eax寄存器。 14.pop数据弹出至指定位置同时esp栈顶寄存器也要发生改变Add函数栈帧被释放。 15.ret是恢复返回地址找到call指令压入的地址返回到main函数栈帧。 16.局部变量被销毁变量c接收返回值 17.后面就是main函数的栈帧销毁过程同Add函数相同 要解决的问题 局部变量是怎么创建的 在栈帧中会分配一块内存空间来存储函数的局部变量。这些局部变量是在函数内部定义的只在函数的生命周期内可见。编译器会根据函数的局部变量声明来确定需要分配多少内存空间。 为什么局部变量的值是随机值 局部变量的值可能会被认为是随机的主要是因为在栈帧创建过程中这些变量并没有经过显式的初始化。当函数栈帧被分配内存并为局部变量分配空间时这些变量的内容实际上是未定义的vs编译器会自动为其初始化为0xcccccc。 函数是怎么传参的传参的顺序是怎么样的 函数传参的过程形参的实例化并不在新的函数栈帧内而是在调用一方函数栈帧内实例化的当该函数要使用该形参的时候是通过地址的方法去寻找该形参变量传参的顺序是从右到左。 形参和实参是什么关系 当调用一个函数时实参的值被传递给函数的形参。这发生在函数的栈帧创建阶段。形参在函数内部被视为局部变量并且其值被初始化为相应的实参值。但是实参的地址和形参的地址是不同的所以形参只是实参的一份临时拷贝修改形参是不能改变实参的。 函数调用是怎么做的 函数栈帧通过call指令先1. 压入返回地址 也就是调用完函数后要执行的下一条指令然后call通过jump转入目标函数待函数使用完ret指令会找到call指令压入的地址值通过这个值返回到main函数的栈帧即调用完函数要执行的下一条指令。 函数调用结束后怎么返回的 利用寄存器的值不会随着函数栈帧被销毁而被销毁的特点利用寄存器将值返回。