【C语言】函数栈帧的创建和销毁

一、准备知识

       1.1寄存器

        32位CPU所含有的寄存器有:数据寄存器指针寄存器变址寄存器段寄存器指令指针寄存器标志寄存器

              1.1.1数据数据寄存器

寄存器作用
EXM: 累加寄存器累加器可用于乘、除、输入/输出等操作
EBX: 基址寄存器它可作为存储器指针来使用
ECX: 计数寄存器在循环和字符串操作时,要用它来控制循环次数
EDX: 数据寄存器在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址

              1.1.指针变址寄存器

寄存器解释
ESP: 堆栈指针寄存器其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶
EBP: 扩展基址指针寄存器其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部
ECX: 源变址寄存器
EDX: 目的指针寄存器

       1.2程序内存分区

在这里插入图片描述

段名存储内容分配方式生长方向读写特点运行态
代码段程序指令、字符串常量、虚函数链表静态分布由低到高只读用户态
数据段初始化的全局变量和静态变量静态分布由低到高可读可写用户态
BSS段未初始化的全局变量和静态变量静态分布由低到高可读可写用户态
动态申请的数据静态分布由低到高可读可写用户态
映射段动态链接库、共享文件、匿名映射对象静态分布由低到高可读可写用户态
局部变量、函数参数与返回值、函数返回地址、调用者环境信息静态分布由高到低可读可写用户态
内核空间储操作系统、驱动系统静态分布都有不能直接访问内核态

  而函数栈帧:就是指函数被调用时,系统会在栈区为该函数开辟一块栈空间,这个栈空间就是该函数的函数栈帧。在这里插入图片描述

       1.3简单汇编指令

              1.3.1通用数据传送指令

                 pop:把字弹出堆栈

                 push:把字压入堆栈

                 move:传送字或字节

              1.3.2算术运算指令

                 sub:减法

                 add:加法

                 cmp:比较(两操作数作减法,仅修改标志位,不回送结果)

              1.3.3目的地址传送指令

                 lea:装入有效地址

              1.3.4串指令

                 rep:当CX/ECX<>0时重复

                 stos:保存串

              1.3.5程序转移指令

                 call:过程调用

注意:为了更好的观察函数栈帧的创建与销毁,最好不用最新版本的编译器,这里用VS2013

二、函数栈帧的创建与销毁

       2.1宏观把握

      代码例子:

#include<stdio.h>
int Add(int x,int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int c=Add(a, b);
	printf("%d", c);
	return 0;
}

      打开调用堆栈
在这里插入图片描述
在这里插入图片描述
      F10狂按直到
在这里插入图片描述
      转到626行观察代码可以发现,mian是被调用的,往上翻阅可以得知是被__tmainCRTStartup调用
在这里插入图片描述
      转到466行__tmainCRTStartup又被mainCRTStartup调用

在这里插入图片描述

所以调用关系为: mainCRTStartup{…__tmainCRTStartup{ …main… }…}

在这里插入图片描述

       2.2细节把控

      接下来让我们康康汇编语言的世界来更深层次的理解函数栈帧的创建与销毁
      F10+右键转到反汇编
在这里插入图片描述

int main()
{
00101400  push        ebp  
00101401  mov         ebp,esp  
00101403  sub         esp,0E4h  //为mian()预开辟的空间
00101409  push        ebx  
0010140A  push        esi  
0010140B  push        edi  
0010140C  lea         edi,[ebp-0E4h]  
00101412  mov         ecx,39h  
00101417  mov         eax,0CCCCCCCCh  
0010141C  rep stos    dword ptr es:[edi]  //从edi开始向下39h空间都改为0CCCCCCCCh  
	int a = 10;
0010141E  mov         dword ptr [a],0Ah  //不显示符号名为dword ptr [ebp-8],0Ah  
	int b = 20;
00101425  mov         dword ptr [b],14h  //不显示符号名为dword ptr [ebp-14h],14h  
	int c=Add(a, b);
0010142C  mov         eax,dword ptr [b]  //将ebp-14h的值给eax
0010142F  push        eax  
00101430  mov         ecx,dword ptr [a]  
00101433  push        ecx  
00101434  call        _Add (01010E1h)  //call指令的一下条指令的指令调用
00101439  add         esp,8  
0010143C  mov         dword ptr [c],eax  //dword ptr [ebp-20h],eax  
	printf("%d", c);
0010143F  mov         esi,esp  
00101441  mov         eax,dword ptr [c]  
00101444  push        eax  
00101445  push        105858h  
0010144A  call        dword ptr ds:[109114h]  
00101450  add         esp,8  
00101453  cmp         esi,esp  
00101455  call        __RTC_CheckEsp (010113Bh)  
	return 0;
0010145A  xor         eax,eax  
}
0010145C  pop         edi  
0010145D  pop         esi  
0010145E  pop         ebx  
0010145F  add         esp,0E4h  
00101465  cmp         ebp,esp  
00101467  call        __RTC_CheckEsp (010113Bh)  
0010146C  mov         esp,ebp  
0010146E  pop         ebp  
0010146F  ret 

              2.2.1汇编代码详解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

所以有时候在定义变量但没有赋值的情况下输出的随机值可能为ccccc等等

在这里插入图片描述
在这里插入图片描述

a和b的位置取决于编译器

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
       F11继续走时候跳转到Add()函数

#include<stdio.h>
int Add(int x,int y)
{
001013C0  push        ebp  
001013C1  mov         ebp,esp  
001013C3  sub         esp,0C0h  
001013C9  push        ebx  
001013CA  push        esi  
001013CB  push        edi  
001013CC  lea         edi,[ebp-0C0h]  
001013D2  mov         ecx,30h  
001013D7  mov         eax,0CCCCCCCCh  
001013DC  rep stos    dword ptr es:[edi]  
	return x + y;
001013DE  mov         eax,dword ptr [x]  //eax,dword ptr [ebp+8]  
001013E1  add         eax,dword ptr [y]  //eax,dword ptr [ebp+0Ch] 
}
001013E4  pop         edi  
001013E5  pop         esi  
001013E6  pop         ebx  
001013E7  mov         esp,ebp  
001013E9  pop         ebp  
001013EA  ret 

      可以发现Add()main()有异曲同工之妙,故不赘述直接上图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

return返回的值暂时存在eax

在这里插入图片描述

call指令的作用就是让Add函数回到main

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
      之后的过程以此类推…

由于作者水平有限,欢迎评论区批评指正:)

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

XiYang-DING

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

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

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

打赏作者

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

抵扣说明:

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

余额充值