从汇编代码探究函数栈帧的创建和销毁的底层原理

这篇具有很好参考价值的文章主要介绍了从汇编代码探究函数栈帧的创建和销毁的底层原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

从汇编代码探究函数栈帧的创建和销毁的底层原理

人,只有在放弃战斗的时候才算输,只要坚持战斗,就还没输 

本文收录于青花雾气-计算机基础

往期回顾

从0到1搞定在线OJ

数据在内存中的存储

计算机存储的大小端模式

目录

一、先导知识

二、函数调用堆栈

三、函数栈帧的创建

1.创建函数栈帧

2.创建变量

3.函数传参

4.函数调用

四、函数栈帧的销毁


大家好,我是纪宁。

这篇博客将从底层原理加汇编代码剖析函数栈帧的创建和销毁的过程

注:本文使用的编译器:Visual Studio 2013(不用更高版本VS的原因:新的编译器为了考虑更多的问题,分装的更加复杂,不容易抽离出函数栈帧的创建过程

编译器不同,存储位置可能会有差异,但思想是一样的

学了这篇文章,你会想清楚曾经一直困扰你很久的一些问题,如

  • 局部变量是怎么创建的
  • 为什么局部变量的值是随机值
  • 函数值怎么传参的?传参的顺序是怎样的
  • 形参和实参是什么关系
  • 函数调用到底是怎么做的
  • 函数调用结束后是如何返回的

一、先导知识

C/C++中内存分为3个区域:栈区、堆区、静态区

不同性质的变量存放在不同的内存区域中,下图是各种变量所在内存中的区域

从汇编代码探究函数栈帧的创建和销毁的底层原理

本文所讲的函数栈帧的创建和销毁过程就是在栈区进行的

栈区存放变量的特点:先存高地址,再存低地址。

销毁变量的时候是先销毁低地址里面的变量,再销毁高地址里面的变量,如图

例如:变量1先创建,再创建变量2......创建变量8;但是销毁的时候,就是先销毁变量8......最后销毁变量1

从汇编代码探究函数栈帧的创建和销毁的底层原理 本文中将用到的两个寄存器:ebp、esp,这两个指针中存放的是地址,用来维护函数栈帧

ebp是栈底指针、esp是栈顶指针正在调用哪个函数,ebp、esp就维护哪个函数的函数栈帧

二、函数调用堆栈

在vs2013中来测试并观察main函数栈帧的创建过程

从汇编代码探究函数栈帧的创建和销毁的底层原理

F10-->调试-->窗口-->调用堆栈  一直按F10直到程序结束,就会出现下面的场景

从汇编代码探究函数栈帧的创建和销毁的底层原理

 可以看到main函数是由626行的那个函数  __tmainCRTStartup函数调用的

从汇编代码探究函数栈帧的创建和销毁的底层原理 而  __tmainCRTStartup 函数又是由466行的   mainCRTStartup 函数调用的,调用的顺序正是由栈顶往栈底

从汇编代码探究函数栈帧的创建和销毁的底层原理

三、函数栈帧的创建

1.创建函数栈帧

有(二)中可知,VS2013中,main函数也是被其他函数调用的,每一次函数调用都要在栈区上分配空间,接下来就通过观察汇编代码来看观看函数栈帧的创建,假设mian函数中还有一个Add函数,来计算两个数的相加的和。

从汇编代码探究函数栈帧的创建和销毁的底层原理

按下F10后不要乱动,右击鼠标,转到反汇编(C语言对应的汇编代码)

从汇编代码探究函数栈帧的创建和销毁的底层原理

这就是这段代码对应的汇编代码 

从汇编代码探究函数栈帧的创建和销毁的底层原理

由于main函数是由 __tmainCRTStartup函数调用的,所以在调用main函数前,main函数的函数栈帧已经创建好了

从汇编代码探究函数栈帧的创建和销毁的底层原理

 push:给栈顶放一个元素从汇编代码探究函数栈帧的创建和销毁的底层原理

push          ebp 将ebp里面的值放在栈顶,同时因为esp指向的是栈顶,所以esp指针也‘上移’

如图

从汇编代码探究函数栈帧的创建和销毁的底层原理从汇编代码探究函数栈帧的创建和销毁的底层原理mov:将前面的值放到后面值的地方

mov        ebp,esp,将ebp指针移动到esp指针的位置

sub:前面的值减去后面的值

sub         esp,0E4h,指针esp减去0E4h(一个8进制的数字),并移动指针esp

从汇编代码探究函数栈帧的创建和销毁的底层原理

由于ebp是栈底指针,esp是栈顶指针,正在调用哪个函数,ebp和esp就维护哪个函数的函数栈帧

后面调用Add函数的时候,ebp、esp就去维护Add函数的函数栈帧。

所以栈区中mian函数的调用空间(预开辟)就开辟好了

从汇编代码探究函数栈帧的创建和销毁的底层原理

连续push三次,将这三个元素依次放到栈顶,压栈三次

同时esp也移动到栈顶

从汇编代码探究函数栈帧的创建和销毁的底层原理

lea:load effecitive address   加载有效地址 

从汇编代码探究函数栈帧的创建和销毁的底层原理

lea             edi,[ebp-0E4h]  把  ebp-0E4h 的地址放到edi里面去,而ebp-0E4h应该就是main函数的栈顶地址

mov           ecx,39h 把39h的值放入eax中去

mov           eax,0CCCCCCCCh 把0CCCCCCCCh的值放入eax中

rep stos     dword ptr es:[edi] 把从 edi(edp-0E4h开始向下ecx(39h)次 dword 的数据全部都改成 eax0CCCCCCCC) (dword(双字节))图例

简单的来讲就要将从 edp-0E4h 到 ebp 中间所有的内容初始化为0ECCCCCCCC

从汇编代码探究函数栈帧的创建和销毁的底层原理

假设初始化完了(没有画全),意思就是中间全部初始化为0CCCCCCCC

此时main函数才开始执行代码

2.创建变量

在代码的反汇编部分,右击鼠标,将显示符号名关掉

一直按F10到Add之前的汇编代码处

从汇编代码探究函数栈帧的创建和销毁的底层原理

mov         dword ptr [ebp-8],5       将5存入  ebp-8 的位置

mov         dword ptr [ebp-14h],2   将2存入  ebp-14h(4+1*16=20(ebp-20))的位置

mov         dword ptr [ebp-20h],0   将0存入  ebp-20h(0+2*16=32(ebp-32))的位置

存储之后的内存图

从汇编代码探究函数栈帧的创建和销毁的底层原理这样就创建了变量a,b,c

3.函数传参

从汇编代码探究函数栈帧的创建和销毁的底层原理mov         eax,dword ptr [ebp-14h]   将ebp-14h(其实就是b的值)的值放入eax(寄存器)里面去

push        eax  eax压栈,将eax放在栈顶

mov         ecx,dword ptr [ebp-8]   将ebp-8(其实就是a的值)的值放入ecx(寄存器)里面去

push        ecx  ecx压栈 ,将ecx放栈顶

从汇编代码探究函数栈帧的创建和销毁的底层原理

传参---先传b,再传a (说明函数传参是从右向左的)

4.函数调用

从汇编代码探究函数栈帧的创建和销毁的底层原理

call--函数调用

执行到此处要按F11进入函数内部 

从汇编代码探究函数栈帧的创建和销毁的底层原理

观察内存后,发现执行完call后,call指令的下一条指令的地址被压栈

当后面执行完Add函数后,通过这个地址可以找到调用位置并继续向下执行代码

从汇编代码探究函数栈帧的创建和销毁的底层原理

再按F11就进入了Add函数,发现前面的指令与main函数里面一样,说明函数栈帧的创建过程是相同的

从汇编代码探究函数栈帧的创建和销毁的底层原理

从汇编代码探究函数栈帧的创建和销毁的底层原理

与刚开始创建mian函数栈帧的过程相同,唯一不同的就是不同函数栈帧开辟的空间大小有差异,这个空间大小取决于编译器

同样,由于正在调用Add函数,ebp和esp就来维护Add函数的函数栈帧

从汇编代码探究函数栈帧的创建和销毁的底层原理同样,在Add函数中开始执行代码

mov         eax,dword ptr [ebp+8]  ebp+8的值赋给eax

add         eax,dword ptr [ebp+0Ch]   将ebp+0Ch的值与eax的值相加并赋值给eax

mov         dword ptr [ebp-8],eax  将eax的值赋给ebp-8

eax是一个寄存器,可以存储变量/变量的值

从汇编代码探究函数栈帧的创建和销毁的底层原理

说明真进行函数调用的时候,形参并不是在Add函数内部创建的,而是在函数调用刚开始的时候,通过压栈,将参数a,参数b传上去了,且参数b是先传的

在使用形参的时候,回头找到了以前压栈的这块空间,说明形参确实是实参的一份临时拷贝

形参并没有真再创建自己的空间

最后将形参计算的结果存入ebp-8中

但是如果在函数里面创建变量的话,它的内存是单独分配的

四、函数栈帧的销毁

从汇编代码探究函数栈帧的创建和销毁的底层原理 先将计算结果ebp-8(z)里面的值存在寄存器中,方便后面将Add的函数栈帧销毁后返回

从汇编代码探究函数栈帧的创建和销毁的底层原理

三次pop,从栈顶删去三个元素

从汇编代码探究函数栈帧的创建和销毁的底层原理从汇编代码探究函数栈帧的创建和销毁的底层原理

从汇编代码探究函数栈帧的创建和销毁的底层原理

将ebp赋为esp(说明esp移动到ebp位置,此时栈顶发生变化,到了ebp的位置)

从汇编代码探究函数栈帧的创建和销毁的底层原理

然后从栈顶删去一个元素放在ebp里面,esp继续向下。

因为此时栈顶放的是main函数的栈底地址,所以,ebp通过这个地址找到了main函数的栈底

从汇编代码探究函数栈帧的创建和销毁的底层原理

此时就从Add函数里面顺利的回到了main函数里面

因为00F83420是调用函数的指令call的下一条指令的地址,所以恰好可以让代码继续运行

这就是上面为什么要将call下一条指令的地址存起来

确保‘走出去’,还能‘找回来’ 继续运行add这一条指令

从汇编代码探究函数栈帧的创建和销毁的底层原理

然后ret(return):将栈顶的那个指针pop(出栈) 

从汇编代码探究函数栈帧的创建和销毁的底层原理esp(栈顶指针)再向下移动从汇编代码探究函数栈帧的创建和销毁的底层原理

此时esp+8,即将栈顶 + 8 就相当于将形参x,y销毁,如图

从汇编代码探究函数栈帧的创建和销毁的底层原理

从汇编代码探究函数栈帧的创建和销毁的底层原理

然后mov 将寄存器eax中存的Add函数的计算结果放在ebp-20h中,即c中

从汇编代码探究函数栈帧的创建和销毁的底层原理

从汇编代码探究函数栈帧的创建和销毁的底层原理此时要进入printf函数,虽然printf已经封装好了,但printf函数的调用还是要经历函数栈帧的创建和销毁这个过程,方法与Add函数相同,这里就不过多赘述

从汇编代码探究函数栈帧的创建和销毁的底层原理那么return 0就是main函数栈帧的销毁过程

从汇编代码探究函数栈帧的创建和销毁的底层原理 最后的结果就是main函数的栈帧也被销毁

从汇编代码探究函数栈帧的创建和销毁的底层原理

学习新知识的过程大多是枯燥和乏味的,放弃很容易,但坚持一定很酷

如果你也能和我一样坚持学习,那我只能说

泰裤辣!文章来源地址https://www.toymoban.com/news/detail-480540.html

到了这里,关于从汇编代码探究函数栈帧的创建和销毁的底层原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • c语言(函数栈帧的创建和销毁)

    前沿:         可能很多人也是第一次听说函数栈帧这个词,想问什么是函数栈帧,理解函数栈帧有什么作用,函数栈帧的创建销毁是什么呢?这章节我们就来了解一下c语言中函数栈帧的创建和销毁。 思维导图: 目录   一、什么是函数栈帧  1.1   函数栈帧: 1.2  栈:  

    2024年02月07日
    浏览(30)
  • 【C语言】函数栈帧的创建与销毁

    今天我们要学习的是函数栈帧的创建与销毁,学完这部分内容,我们可以解决下面的几个问题: 局部变量是怎么创建的? 为什么局部变量的值是随机值? 函数是怎么传参的?传参顺序是怎样的? 形参和实参是什么关系? 函数调用是怎么做的? 函数调用是怎么做的? 函数调

    2024年02月09日
    浏览(28)
  • C语言——详解函数栈帧的创建和销毁

    为了深入学习C语言,也为了方便理解,我学习了函数栈帧。函数栈帧的创建和销毁能够让我更加深刻的了解编程逻辑和语法。我们学习语法和编程逻辑都是基于封装好的知识上得。因此,我们有必要对函数栈帧的创建和销毁进行学习。本篇博客将用来介绍函数栈帧的创建和销

    2024年02月12日
    浏览(30)
  • C语言-------函数栈帧的创建和销毁------剖析描骨

    🎂        ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂    🎂      作者介绍:                              🎂🎂        🎂 🎉🎉🎉🎉🎉🎉🎉              🎂           🎂作者id:老秦包你会,         🎂 简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂

    2024年02月14日
    浏览(25)
  • 【C语言__函数栈帧的创建和销毁__复习篇9】

    目录 前言 一、知识补充 二、分析创建和销毁的过程 三、前言问题回答 本篇主要讨论以下问题: 1. 编译器什么时候为局部变量分配的空间 2. 为什么局部变量的值是随机的 3. 函数是怎么传参的,传参的顺序是怎样的 4. 形参和实参是什么关系 5. 函数调用是怎么做的 6. 函数调

    2024年04月25日
    浏览(29)
  • 学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁】

    ========================================================================= 相关代码gitee自取: C语言学习日记: 加油努力 (gitee.com) ========================================================================= 接上期: 学C的第十天(继续深入学习函数、函数递归、练习)-CSDN博客 ============================================

    2024年02月04日
    浏览(29)
  • 打通你学习C语言的任督二脉-函数栈帧的创建和销毁(上)

      🌈个人主页:  Aileen_0v0 🔥系列专栏: C语言学习 💫个人格言: \\\"没有罗马,那就自己创造罗马~\\\" 待解决疑惑: 局部变量是怎么创建的? 为什么局部变量的值是随机值? 函数是怎么传参的?传参的顺序是怎样的? 形参和实参是什么关系? 函数调用是怎么做的? 函数调用是结束后怎么返

    2024年02月05日
    浏览(27)
  • 探秘函数栈帧:『 揭开函数栈帧创建与销毁的神秘面纱 』

    .. 目录 知识点回顾 一、什么是栈帧(堆栈帧)? 1.内存布局 2.常用寄存器 3.汇编指令 👇👇对于栈的详细介绍 : 👇👇函数栈帧的介绍: 二、函数调用中的栈帧 1.探究main函数栈帧的创建 2.对main函数中的代码进行分析 3.探究Add函数栈帧的创建  三、函数栈帧的销毁过程 博客引

    2024年02月06日
    浏览(38)
  • 【C语言】函数栈帧的创建和毁销

    大家好,我是深鱼~ 目录 一、寄存器 二、栈区  三、函数栈帧的创建 1.为main函数开辟栈帧  2.在main函数中创建变量 3.调用Add函数前的准备  4.为Add函数开辟栈帧  5.在Add函数中创建变量并运算 四、函数栈帧的销毁 6.Add函数栈帧的销毁 7.返回main函数栈帧 【前言】 前期学习的时

    2024年02月14日
    浏览(27)
  • 函数栈帧的创建和毁销【C语言版】

    大家好,我是深鱼~ 目录 一、寄存器 二、栈区  三、函数栈帧的创建 1.为main函数开辟栈帧  2.在main函数中创建变量 3.调用Add函数前的准备  4.为Add函数开辟栈帧  5.在Add函数中创建变量并运算 四、函数栈帧的销毁 6.Add函数栈帧的销毁 7.返回main函数栈帧 【前言】 前期学习的时

    2024年02月15日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包