首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >函数调用时,CPU在背后偷偷干了什么?看完这篇我悟了!

函数调用时,CPU在背后偷偷干了什么?看完这篇我悟了!

作者头像
码途随笔
发布2026-01-12 19:59:28
发布2026-01-12 19:59:28
610
举报

前期学习的时候,我们可能有很多困惑? 比如:

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

学会和明白函数的创建和销毁,就都知道了

一、预备知识

相关寄存器 eax:通用寄存器,保留临时数据,常用于返回值 ebx:通用寄存器,保留临时数据 ebp:栈底寄存器 esp:栈顶寄存器 eip:指令寄存器,保存当前指令的下一条指令的地址

mov:数据转移指令 push:数据入栈,同时esp栈顶寄存器也要发生改变 pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变 sub:减法命令 add:加法命令 call:函数调用,1. 压入返回地址 2. 转入目标函数 jump:通过修改eip,转入目标函数,进行调用 ret:恢复返回地址,压入eip,类似pop eip命令

二、函数栈帧的创建和销毁解析

案例:

代码语言:javascript
复制
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 0;
	int b = 0;
	int c = 0;
	c = Add(a, b);
	return 0;
}

先为main函数开辟空间

正在调用那个函数,ebpesp就调用哪个函数,ebpesp之间的空间一直在变

执行时,每个函数都要开辟空间,如下图所示

2.1 push

从调用main函数的函数开始

转到反汇编

先执行第一条

过程如下:esp往上移

验证:打开内存,可以看到esp里面装的就是ebp里面的值

2.2 move

ebp指向esp所在的位置

验证:

2.3 sub

esp往上走,就可以为main函数开辟栈帧

2.4 压栈

push一次,esp往上走一次,esp里面的值依次装ebxesiedi

2.5 0xCC…h的诞生

详细翻译:从edi开始向下的ecx39h个(空间),这么多个double word(4个字节)的数据改成0CCCCCCCChlea语句执行完edi变为edp-0E4h,最后执行rep sto 语句后edi变为espedp-0E4hedp之间的空间变为0CCCCCCCCh

2.6 部分代码的实现

int a=10为例,把10赋值到ebp-8里面去

因为a为整型类型,所以ebp-8存储变量a(4个字节)

b、c同理

2.7 函数传参

翻译:由于ebp-14h里面存放的是b的值,先放到eax里面,再压栈进去,esp往上走(先传b变量),后面a同理

call:函数调用,1. 压入返回地址 2. 转入目标函数

当进入到add里面去,需要返回地址,从这个地址回来后再往下执行

按F11,再往下执行进入add函数里面

跟前面main函数分配函数栈帧做的准备一样

当运行z=x+y

刚好ebp+8对应aebp+12对应b,参数是从右向左传,先b再a,并且形参根本不是在add函数内部创建的,找的是传参压进来的空间(验证了形参是实参的临时拷贝,改变形参不会影响实参)

2.8 函数返回

上述,z已经算出来了,该如何返回

ebp-8的值放到eax寄存器当中,寄存器不会销毁(相当于全局变量)

2.9 回收空间

pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变,把数据弹出到对应的寄存器中,esp依次往后加4,

然后ebp赋给esp

再弹出edp,edp装的是main函数原来的地址,又返回edp这个寄存器了,使得edp返回到原来main函数栈底的位置,esp接着往下走,main函数的空间又由espedp维护

回到了main函数的空间,应该从call指令下一条语句开始执行,执行ret指令,之所以存放地址,就是为了能够回来从call指令下一跳指令回来

接着,esp加8,往下走,把形参的空间返回去,并且把eax的值放到ebp-20h中(也就是变量c中)

三、答疑

回答前面所提到的问题

局部变量是怎么创建的

首先为函数分配好栈帧空间,栈帧空间里面初始化一部分之后,然后局部变量在栈帧中分配空间

为什么局部变量的值是随机值?

因为里面放的是随机值0xCCCCCCCCh,如果初始化的话,就把随机值覆盖掉了

函数是怎么传参的?传参的顺序是怎样的?

其实还没调用函数的时候,形参就从右向左压栈压进去了,当进入形参函数以后,通过指针的偏移量来访问形参

形参和实参是什么关系?

形参是在压栈时开辟的空间,它和实参只是值上面是相同的,空间是独立的,形参是实参的临时拷贝

函数调用是怎么做的?

函数调用是结束后怎么返回的?

在调用函数之前就把Call指令的下一条指令的地址压栈压进去,把调用这个函数的上一个函数的edp压栈压进去了,当函数调用完放回时,弹出edp后,edp里面装的更新为上一个main函数的edp,跳到Call指令后一个地址,esp回到原来的栈帧空间,返回值是通过寄存器的方式带回来的

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-10-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、预备知识
  • 二、函数栈帧的创建和销毁解析
    • 2.1 push
    • 2.2 move
    • 2.3 sub
    • 2.4 压栈
    • 2.5 0xCC…h的诞生
    • 2.6 部分代码的实现
    • 2.7 函数传参
    • 2.8 函数返回
    • 2.9 回收空间
    • 三、答疑
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档