【C语言】函数调用过程中,寄存器是如何维护栈区中的函数栈帧的?

一、说明

每个编译器观察到的函数栈帧的创建和销毁过程都是有差异的,但大体相同,在这里我们使用的是VS2013。版本较新的编译器如VS2019,封装过程更复杂,不容易观察;较早版本编译器如VS6.0,观察过程更简单、直观和易于理解。

二、寄存器与栈区中的函数栈帧

文章:寄存器在计算机的存储设备中的地位 寄存器集成在CPU上的,计算机中存在多个寄存器,有eax、ebx、ecx、edx、rax、rbx、rcx、rbp、rsp、esi、edi、ebp、esp等等,其中ebp和esp是用来存放地址、维护函数栈帧的。

我们知道:每一个函数调用,都会在栈区创建一块内存空间,这块空间是独立的,不受其他栈区的内存空间影响。这是因为ebp和esp维护的作用,esp指向这个被调用的函数栈帧空间的栈顶,ebp指向栈底,ebp和esp中间的空间就是这个函数的空间。栈的使用默认是先使用高地址到低地址的,ebp指向的栈底其实是高地址,esp指向的栈顶其实是低地址。

下面这段程序例子:
在这里插入图片描述
右边画的图仅仅只是简化版。这里要说明一点,其实main()也是由其它函数调用的,分别是mainCRTStartup和__tmainStartup函数,图中所画最下边两块空间就是它俩的,因为不是重点就随便画了一小块,知道有这么回事就行。

另外,当main()函数中调用其它函数时,ebp和esp会去维护被调用的函数,通过±地址值的方式向低地址移动到调用函数的栈帧,当函数调用结束,ebp和esp同样会通过记录的地址值,或是±地址值返回到main函数的函数栈帧。

要想知道完整过程和细节,需要debug反编译后看汇编代码,一条条去执行去理解,下图分别是main函数和Add函数的反汇编代码(每条C语句下面对应它的反汇编代码)。
在这里插入图片描述

上面说过,编译器版本不同底层实现方式也不同,我用的VS2022版本很新,反编译也并没有看到ebp和esp这两个寄存器,其实在VS2013下能看到确实是这两个寄存器来维护,而在VS2022则是rbp和rsp,不过这并不影响我们理解,其余寄存器为辅助用。

这里我们要知道几个重要的指令:

  1. push指令是压栈操作,每次压栈rsp都会指向新的栈顶;
  2. lea指令全称是load effective address。
  3. mov是将某个值存入到某个地址中,例如int a = 10;的汇编代码,dword意思是double word,1个word是两个字节,那么dword就是4个字节空间,int正是4个字节。0Ah,h代表hex十六进制,0A换算十进制就是10,rbp + 4是保存10的某个地址,+4代表向高地址移动4个字节的位置。
  4. pop是出栈操作,每次出栈rbp都会重新指向新的栈底;
  5. call是函数调用操作,其后一串地址是函数的地址。
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

念来过倒字名qwq

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

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

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

打赏作者

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

抵扣说明:

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

余额充值