详细分析 —— 函数栈帧的创建与销毁(C语言进阶)

目录

准备阶段 

栈顶指针和栈底指针

函数栈帧的创建与销毁:

1、大体分析思路(汇编代码)

2、main函数的创建 

具体过程:

画图:

 3、储存变量

具体过程

画图

3、 ADD函数的创建与销毁

 传参

 ADD函数的创建

 变量的创建

ADD函数的销毁 

4、打印c的值

具体过程 

画图 

5、main函数的销毁 

具体过程 

 画图



准备阶段 

如果想上手操作并看到过程,不建议跳过准备阶段部分,仔细阅读这个部分(直接从寄存器看,也可以)。

2019版本 - VS编译器

用到VS编译器中的:反汇编窗口、内存窗口、监视窗口。

按键盘上的F10(逐过程调试) —— 点击上方的调试 —— 窗口 —— 反汇编 —— 内存 —— 监视

 把反汇编、内存、和监视窗口打开后是下面这样:

 

接下来按F10来一步一步的调试,观察内存窗口和监视窗口来分析执行的步骤。

栈顶指针和栈底指针

寄存器:

有eax,ebx,ecx,edx,今天重点的寄存器是:ebp(栈底指针),esp(栈顶指针),这两个寄存器是用来维护函数栈帧的。

注意:寄存器是放在cpu中的,不在main函数中

在不同的编译器下,函数调用的过程中栈帧的创建是略有差异的,具体的细节取决于编译器的实现。

在观察函数栈帧的创建与销毁的过程中有两个非常重要的指针  esp(栈顶指针)和ebp(栈底指针),他们两个是用来维护函数栈帧的。观察时,主要看这两个指针(esp,ebp),在指针esp上方的内存是可以存放数据的内存。

函数栈帧的创建与销毁:

1、大体分析思路(汇编代码)

用一段简单的代码分析函数栈帧的创建与销毁

 下面的代码是上面代码的汇编代码(更容易观察栈帧创建和销毁的过程):

 ADD函数的创建与销毁

2、main函数的创建 

具体过程:

int main()
{
005518D0  push        ebp                //压栈:把ebp的放在栈的上面,然后esp上移一个整型
005518D1  mov         ebp,esp            //把ebp的值改成esp的值
005518D3  sub         esp,0E4h           //把esp的值改成(esp – 0E4h)(0E4h为228)的值
005518D9  push        ebx                //压栈:把ebx的放在栈的上面,然后esp上移一个整型
005518DA  push        esi                //压栈:把esi的放在栈的上面,然后esp上移一个整型
005518DB  push        edi                //压栈:把edi的放在栈的上面,然后esp上移一个整型
005518DC  lea         edi,[ebp-24h]      //edi中的值变成:ebp-24h
005518DF  mov         ecx,9              //ecx中的值变成:9
005518E4  mov         eax,0CCCCCCCCh     //eax中的值变成:0CCCCCCCh
005518E9  rep stos    dword ptr es:[edi] //从edi存放的地址到ebp存放的地址全部初始化为0CCCCCCCh
005518EB  mov         ecx,55C003h        //ecx中的值变成:55C003h
005518F0  call        0055131B           //

说明:

main函数也是一个函数,既然是函数那么就有函数栈帧的创建与销毁

Push——压栈(给栈顶放一个元素)、move——把后一位的值赋予前一位、sub——减去一个数、dword —— 一个整型

画图:

 3、储存变量

具体过程

	int a = 10;
005518F5  mov         dword ptr [ebp-8],0Ah    //把ebp-8中的值变成0Ah
	int b = 20;
005518FC  mov         dword ptr [ebp-14h],14h  //把ebp-14h中的值变成14h 
	int c = 0;
00551903  mov         dword ptr [ebp-20h],0    //把ebp-20h中的值变成0

	c = ADD(a, b);

画图

3、 ADD函数的创建与销毁

 传参

具体过程

	c = ADD(a, b);
0055190A  mov         eax,dword ptr [ebp-14h] //eax中放ebp-14h的值
0055190D  push        eax                     //压栈:
0055190E  mov         ecx,dword ptr [ebp-8]   //ecx中放ebp-8的值
00551911  push        ecx                     //压栈:
00551912  call        005513B6                //压栈:放005512B6

画图 

 

 ADD函数的创建

具体过程 

int ADD(int x, int y)
{
00551770  push        ebp               //压栈:
00551771  mov         ebp,esp           //把ebp的值改成esp的值
00551773  sub         esp,0CCh          //把esp的值改成(esp – 0CCh)(0CCh为204)的值
00551779  push        ebx               //压栈:
0055177A  push        esi               //压栈:
0055177B  push        edi               //压栈:
0055177C  lea         edi,[ebp-0Ch]     //edi中的值变成:ebp-0Ch
0055177F  mov         ecx,3             //ecx中的值变成:3
00551784  mov         eax,0CCCCCCCCh    //eax中的值变成:0CCCCCCCh
00551789  rep stos    dword ptr es:[edi]//edi存放的地址到ebp存放的地址全部初始化成0CCCCCCCh
0055178B  mov         ecx,55C003h       //ecx中的值变成:55C003h
00551790  call        0055131B          //

  画图

 其中的 005513B6 是call指令的下一条地址

 变量的创建

具体过程

	int z = 0;
00551795  mov         dword ptr [ebp-8],0    //为z开辟空间:把ebp-8改成0
	z = x + y;
0055179C  mov         eax,dword ptr [ebp+8]  //把ebp+8(形参x)的中的值放在eax中
0055179F  add         eax,dword ptr [ebp+0Ch]//eax加上ebp+0Ch(形参y)中的值
005517A2  mov         dword ptr [ebp-8],eax  //把eax(两个数相加的值)放入ebp-8(z)中
	return z;
005517A5  mov         eax,dword ptr [ebp-8]  //把返回值(ebp-8)放入eax中

画图 

ADD函数的销毁 

具体过程 

005517A8  pop         edi      //pop——出栈:放出edi
005517A9  pop         esi      //出栈:放出esi
005517AA  pop         ebx      //出栈:放出ebx
005517AB  add         esp,0CCh //esp加上0CCh(16进制)
005517B1  cmp         ebp,esp  //
005517B3  call        00551244 //
005517B8  mov         esp,ebp  //esp的值变成ebp
005517BA  pop         ebp      //出栈:弹出栈顶的ebp(main)
005517BB  ret                  //回到地址005512B6(call指令的下一条指令)

画图 

4、打印c的值

具体过程 

00551917  add         esp,8                   //esp 加 8
0055191A  mov         dword ptr [ebp-20h],eax //ebp-20(c)的值改成eax中的值

	printf("%d\n", c);
0055191D  mov         eax,dword ptr [ebp-20h] //eax中的值改成ebp-20h(c)中的值
00551920  push        eax                     //压栈:
00551921  push        557B30h                 //压栈:
00551926  call        005510CD                //进入printf函数(和ADD函数的创建和销毁一样)
                                              //这个函数执行的是在屏幕上打印C的值
0055192B  add         esp,8                   //esp加8

画图 

5、main函数的销毁 

具体过程 

	return 0;
0055192E  xor         eax,eax  //
}
00551930  pop         edi      //出栈
00551931  pop         esi      //出栈
00551932  pop         ebx      //出栈
00551933  add         esp,0E4h //esp加上0E4h(228)
00551939  cmp         ebp,esp  //
0055193B  call        00551244 //
00551940  mov         esp,ebp  //esp的值改成ebp的值
00551942  pop         ebp      //出栈:把ebp弹出来
00551943  ret                  //回到call的下一条语句

 画图

 谢谢观看(如有错误欢迎指正)

  • 34
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT技术博主-方兴未艾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值