首页 > 作文

C语言函数栈帧的创建和销毁详解

更新时间:2023-04-05 01:12:52 阅读: 评论:0

目录
写在前面add函数的调用函数传参add函数栈帧的创建add函数栈帧的销毁main函数栈帧的销毁总结

写在前面

我们知道,每一次函数调用都需要在栈区上为其开辟一块空间电教,这块空间就叫做这个函数的栈帧。

而栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。

这样我们就了解了寄存器ebp和寄存器esp中存放的是地址,这两个地址是用来维护函数栈帧的。比如:调用main函数, 我们为main函数分配栈帧空间, 那么栈帧维护如下:

下面我们通过一段代码分析一下,函数栈帧创建和销毁的过程:(栈帧这部分内容在不同的编译器上实现存在差异, 但是思想大致都是一致的。本文是在vs2013编译器下实现的。)

#include <stdio.h>int add(int x, int y){int z = 0;z = x + y;return z;}int main(void){int a = 10;int b = 20;int ret = 0;ret = add(a, b);//计算a+bprintf("%d\n", ret);return 0;}

我们在调试过程打开调用堆栈

可以看出,main函数是在__tmaincrtstartup函数内部被调用的,而__tmaincrtstartup函数又是在maincrtstartup函数内部调用的。

为了能更加清楚的看到栈帧创建和销毁的过程,我们转到上面代码对应的反汇编代码:

int main(void){009d3f40  push        ebp  //将edp压入栈帧009d3f41  mov         ebp,esp  //将esp的值赋给edp009d3f43  sub         esp,0e4h  //esp-0e4h009d3f49  push        ebx  009d3f4a  push        esi  009d3f4b  push        edi  009d3f4c  lea         edi,[ebp+ffffff1ch]  009d3f52  mov         ecx,39h  009d3f57  mov         eax,0cccccccch  009d3f5c  rep stos    dword ptr es:[edi]  int a = 10;009d3f5e  mov         dword ptr [ebp-8],0ah  int b = 20;009d3f65  mov         dword ptr [ebp-14h],14h  int ret = 0;009d3f6c  mov         dword ptr [ebp-20h],0  ret = add(a, b);//计算a+b009d3f73  mov         eax,dword ptr [ebp-14h]  009d3f76  push        eax  009d3f77  mov         ecx,dword ptr [ebp-8]  009d3f7a  push        ecx  009d3f7b  call        009d11f9  009d3f80  add         esp,8  009d3f83  mov         dword ptr [ebp-20h],eax  printf("%d\n", ret);009d3f86  mov         esi,esp  009d3f88  mov         eax,dword ptr [ebp-20h]  009d3f8b  push        eax  009d圣诞主持词3f8c  push        9d5860h  009d3f91  call        dword ptr ds:[009d9118h]  009d3f97  add         esp,8  009d3f9a  cmp         esi,esp  009d3f9c  call        009d1140  return 0;009d3fa1  xor         eax,eax  }009d3fa3  pop         edi  009d3fa4  pop         esi  009d3fa5  pop         ebx  009d3fa6  add         esp,0e4h  009d3fac  cmp         ebp,esp  009d3fae  call        009d1140  009d3fb3  mov         esp,ebp  009d3fb5  pop         ebp  009d3fb6  ret  

main函数的调用 main函数栈帧的创建

经过刚才我们的理解,在准备调用main函数的时候,调用main函数的那个函数的栈帧已经开辟好了。

然后将ebp压入栈帧,保存了指向栈底的ebp的地址,而此时esp指向新的栈顶位置;接着将esp的值赋给了ebp,产生了新的ebp;用esp减去一个16进制数0e4h(这里就是为main函数预开辟空间)。紧接着三个压栈指令,分别将ebx,esi,edi,压入栈帧。加载完有效地址以后,将为main函数预开辟空间全部初始化为0xcccccccc。最后创建了三个局部变量a,b,ret并进行了初始化。

add函数的调用

函数传参

将b的值存入寄存器eax中,再将eax压入栈中;将美体小铺好用吗a的值存入寄存器ecx中,再将将ecx压入栈中;这里看出参数是从右向左传递的。紧接着执行call指令,这里就是调用add函数,同时将call指令的下一条指令的地址压入栈中,然后执行call指令的时候按f11 , 就进入了add函数内部。

add函数栈帧的创建

首先将main()函数的ebp压入栈,保存指向main()函数栈帧底部的ebp清明节手抄报怎么画的地址,此时esp指向新的栈顶位置;将esp的值赋给ebp,产生新的ebp,即add()函数栈帧的ebp;给esp减去一个16进制数0e4h,这里是为add()函数预开辟空间;紧接着三个压栈指令,分别将保山师范高等专科学校ebx,esi,edi,压入栈帧。加载完有效地址以后,将为add函数预开辟空间全部初始化0xcccccccc。在紧接着创建了变量z,将形参的a和b相加的结果存储到z中;最后将结果存储到eax寄存器中,通过寄存器带回了函数的返回值。

add函数栈帧的销毁

edi、esi、ebx依次出栈,esp 会向下移动;然后将ebp的值赋给esp,使esp指向ebp指向的地方;接着ebp 出栈,同时将出栈的内容给ebp,此时ebp又指向了main函数栈帧的底部,最后执行ret 指令,表示出栈一次,并跳转到出栈的内容的地址处,也就是call指令的下一条指令处。

main函数栈帧的销毁

main函数栈帧的销毁和add函数栈帧销毁的过程的思想都是一样的,这里就不做多赘述了。

总结

通过上面的例子,我们知道了局部变量是如何创建的,知道了为什么创建局部变量不初始化,会导致里面的内容是随机值;对函数是如何传参的,以及传参顺序是如何也有了较为深入的了解。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注www.887551.com的更多内容!

本文发布于:2023-04-05 01:12:51,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/76d6513b36a9f03dfb464f6b5b6bdcb3.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:C语言函数栈帧的创建和销毁详解.doc

本文 PDF 下载地址:C语言函数栈帧的创建和销毁详解.pdf

标签:函数   寄存器   地址   指令
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图