深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2

简介: 深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2

深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1

https://developer.aliyun.com/article/1456960



3.main函数中的核心代码

接下来执行以下三句代码:

b1b75cba216a4e70b08a80c0d0c2d818.png



以a为例子,观察下图:


0d18fa0482484a3aaa218283e26effe1.png


       可得出以下图解:

533b99114b434fe79df2f661d92ff5a8.png



然后接下来执行以下指令:


38541978f4d94174b2664db20ad2934e.png


首先来看前两条指令:


d3b6e64e78b5410f8b04c11f2a776e05.png



mov 是一个指令,用于将数据从一个位置复制到另一个位置。
eax 是一个32位的寄存器,用于存储通用数据。
dword ptr 是一个修饰符,用于指示后面的操作数应该被视为32位的双字(即4个字节)。
[ebp-14h] 是一个内存引用,它指向位于基址指针 ebp 减去 14h(20个字节)的位置。基址指针 ebp 是一个用于存储局部变量和函数参数的寄存器。
综上所述,这行代码的作用是将位于 ebp-14h 地址处的32位数据加载到 eax 寄存器中



159c0ed768bf434fab4fae5a2c5afddd.png


push 是一个指令,用于将数据压入堆栈中。
eax 是之前加载了数据的寄存器。

综上所述,这行代码的作用是将 eax 寄存器中的值(20)压入堆栈中


所以,后两条指令同理③④

af3c6326d6654caaa1101c728b6f57d6.png



       将位于 ebp-8 地址处的32位数据加载到 ecx 寄存器中,将 ecx 寄存器中的值(10)压入堆栈中


图解:


892e812cd0ae4a11b6924d0f8f2c0b99.png


🧨call指令🚩

函数调用过程


8fd5e41c2fd14d61993352c42895ffc1.png


call 是一个指令,用于调用一个函数或子程序。它的作用是将当前指令的下一条指令的地址(返回地址)压入堆栈,并跳转到指定的函数或子程序的地址执行。

按f11,通过call指令就会进入Add函数里面去了(并未真正进入,还要再按一次f11)


1f472cc11e3949bcbef980d51ca8821d.png


    call 指令是要执行函数调用逻辑的,在执行call指令之前先会把call指令的下一条指令的地址进行压栈操作,这个操作是为了解决当函数调用结束后要回到call指令的下一条指令的地方,继续往后执行


因此,call  的作用是将当前指令的下一条指令的地址压入堆栈(00C21450),并跳转到地址为 00C210E1 的函数或子程序的入口点执行



3e6752a31442448f86e792a64ed5a84d.png

4.Add函数栈帧的创建

       再按下f11,这时候才是真正来到add函数内,前面那一堆汇编代码跟main函数栈帧创建逻辑是一样的。


反汇编代码:


fb296a6440b34f2bac8fb6c070e3a46c.png


前提说明


d4086e6b28d2490dbd70035ff55a44d1.png


图解:


001d7c9cf51b4dd7af0382d0db6e339a.png


5.Add函数中的核心代码🎯

反汇编代码:


bb982ceeb5734345b485afeeb73d14fd.png

①:将值0(初始化z)存储到位于内存中的地址 ebp-8 处的双字(32位)数据中。


②将位于内存中地址 ebp+8 处的双字(32位)数据(当前位置的值为10)加载到寄存器 eax 中。


③将位于内存中地址 ebp+0Ch 处的双字(32位)数据(当前位置的值为20)与寄存器 eax 中的值相加,并将结果存储(两数相加的结果为30)回 eax 寄存器中。


④位于当前堆栈帧中相对于基址寄存器 ebp 偏移 8 字节的内存位置的值(当前值为30)复制到寄存器 eax 中


图解:

6ba2e63080274818ba925e1c2d915e6d.png



6.Add函数栈帧的销毁

代码:

58fc6049c05e4c7796d1c0f591608e3c.png



这句代码的意思是:


       将位于 ebp-8 地址处的32位数据(值为30)加载到寄存器 eax 中,因为函数出去之后,值就销毁了,但是如果放在寄存器eax内就安全了,相当于用了一个全局的寄存器把返回值保存起来,回到主函数main再用。


fb861b32265c4f3e9eaa5c7d14579cab.png


然后pop三次,把三个寄存器的地址分别弹出:


8934740f1f4e4076ba80841c2c89d956.png


接着 mov esp,ebp,就是把ebp当前地址赋值给esp:

1612ed4d17914945ade38882beb6b710.png



接着pop ebp,此时ebp回到main函数函数栈帧的栈底:


726a36a855f149efb5f6885bab36ae48.png


       说明此时Add函数已经销毁了。


此时最重要的一条指令来了:


       当pop ebp之后,只是让我们找到了esp和ebp的栈帧空间,但是当我回到main函数的时候,还应该从call指令的下一调指令的地址开始执行,所以此时恰好栈顶上就放着这个地址



deff6fc4ac274528aeec0168255d5bb0.png

       这个ret指令return返回的时候,这个指令其实就是从栈顶弹出了call指令下一条指令的地址,然后跳那去了,接着F10走一下,回来main函数内:


258b3e7b0ab048e2aad7f05393154e5d.png


       存这个地址(00C21450)就是当函数调用完之后还能回来,从call指令的下一条指令的地址开始执行。


所以图解是这样的:



02c7b2a5b5114eb2a56c1e85101f272e.png

关于形参变量空间的释放:


358228cab60d4bf5b9beb90ef22dfa90.png




返回值是怎么带回来:先把值委托到eax寄存器内,接着回到main函数内部赋值

bafde95d8640464aa5f034eacad51b4c.png





       经过esp+8之后,关于x和y两个形参空间的变量就已经销毁,还给操作系统了。


关于main函数的销毁跟上述Add函数的销毁逻辑相似,也不累赘地列举了。

总结:

1.局部变量是如何创建的?


     

首先为main函数分配栈帧空间,然后在栈帧空间内初始化一部分空间之后,给局部变量在该栈帧空间内分配一点空间

2.为什么局部变量不初始化内容是随机的?


 

因为随机值是我们放进去的,如果局部变量给它们初始化,那就是把随机值覆盖了。

3.函数调用时参数时如何传递的?传参的顺序是怎样的?


   

当我要调用那个函数的的时候,就已经push,push,把这两个参数从右向左开始压栈压进去,当我们进入形参函数Add的时候通过指针的偏移量找回来找到了形参


4.形参和实参的关系是什么呢?


     

形参确实是我在压栈的时候开辟的空间,形参和实参只是值是相同的,空间是独立的,所以形参是实参的一份临时拷贝,改变形参不会影响实参

5.函数调用结束后是如何返回的?

我们在调用之前就已经把call指令的下一条指令的地址压栈压进去了,当函数调用完要返回的时候,弹出ebp就能找到原始上一个函数调用的ebp,然后指针往下走的时候就能中找到esp的地址,接着跳转到call指令下一条指令的地址,返回值是通过寄存器的方式带回来的

     

相关文章
|
6天前
|
C语言 图形学 C++
|
3天前
|
自然语言处理 C语言 C++
程序与技术分享:C++写一个简单的解析器(分析C语言)
程序与技术分享:C++写一个简单的解析器(分析C语言)
|
3天前
|
程序员 编译器 C语言
详解C语言入门程序:HelloWorld.c
详解C语言入门程序:HelloWorld.c
8 0
|
3天前
|
机器学习/深度学习 C语言 Windows
程序与技术分享:C语言学生宿舍管理系统代码(可运行)
程序与技术分享:C语言学生宿舍管理系统代码(可运行)
|
6天前
|
程序员 C语言 C++
【C语言】:柔性数组和C/C++中程序内存区域划分
【C语言】:柔性数组和C/C++中程序内存区域划分
9 0
|
2月前
|
存储 算法 数据处理
C语言中的顺序结构程序
C语言中的顺序结构程序
21 1
|
24天前
|
程序员 C语言 C++
C语言学习记录——动态内存习题(经典的笔试题)、C/C++中程序内存区域划分
C语言学习记录——动态内存习题(经典的笔试题)、C/C++中程序内存区域划分
47 0
|
2月前
|
C语言
c语言循环设计程序结构
c语言循环设计程序结构
19 0
|
2月前
|
程序员 C语言
使用指针变量作为函数参数的C语言程序实例
使用指针变量作为函数参数的C语言程序实例
25 0
|
2月前
|
C语言
循环的应用--猜数字游戏、关机程序【c语言篇】
循环的应用--猜数字游戏、关机程序【c语言篇】
36 0