【研究空间复用及函数调用问题】

news2025/1/31 11:08:38

本篇总结函数调用过程会存在的一些奇怪现象,空间复用问题,其实本质上涉及函数调用的底层原理,理解函数栈帧的创建和销毁这样的问题直接迎刃而解。

  • 1.空间复用问题
    • 案例1
    • 案例2
  • 2.函数调用过程不清晰问题
    • 案例3
  • 3.总结

1.空间复用问题

案例1

我们先来看一个代码:

void F1()
{
	int a = 10;
	printf("%p\n", &a);
}
void F2()
{
	int b = 10;
	printf("%p\n", &b);
}
int main()
{
	F1();
	F2();
	return 0;
}

F1和F2函数的操作基本一样,你说它们所开辟的空间的地址是同一块吗?
或者说,a和b的地址是一样的吗?

结果是:一样的。为什么呢?

在这里插入图片描述
我们知道调用函数需要给函数开辟栈帧,也就是开辟空间,而栈帧是在堆区开辟的
当函数使用完后,该函数栈帧就要销毁。

但不是真正意义上的销毁,而是把使用该空间的权限还给操作系统,这片区域不再受你操控。

所以我们在调用F1()函数时,操作系统先给F1()开辟栈帧,给变量a分配内存。然后当F1()函数结束时,该空间又被操作系统收回
接着又调用F2()函数,操作系统又将刚刚收回的空间又分配给F2()函数。
所以F1函数和F2函数使用的空间地址是一样的,变量a和变量b的地址也就是一样的。

在这里插入图片描述

案例2

这是一个阶乘递归的代码

long long Fac(size_t N)
{
	if (0 == N)
		return 1;

	return Fac(N - 1) * N;
}
int main()
{
	long long n;
	Fac(n);
	return 0;
}

请问它的空间复杂度和时间复杂度是多少呢?

函数Fac每次调用都会返回Fac(n-1)*n,直到N==0时才返回1.
也就是递归了n次,所以时间复杂度为O(n).
而空间复杂度呢?
因为Fac函数每次调用自己都会开辟一个函数栈帧,调用了n次,所以开辟了n个空间。
所以空间复杂度也是O(n).
在这里插入图片描述
那想一下,Fac(n)与Fac(n-1)与Fac(n-2)…等函数的空间地址是同一块空间吗?

综合上面的案例我们应该判断它们不是同一块空间的。

为什么呢?
上面的案例是F1函数调用完,结束后,再调用的F2函数
而阶乘递归是属于嵌套调用,每个函数都还没完全结束就又调用另一个函数了,所以它们开辟的空间肯定不一样,它们各自使用的空间都没有被操作系统收回过,怎么可能有其他函数又去占用这块空间呢。

而对案例修改一下,也可以让F1和F2函数的地址不一样,也就是在F1函数的内部去调用函数F2,这样它们的空间就不会重复了。

如下所示:

void F1()
{
	int a = 10;
	printf("%p\n", &a);
	F2();
}
void F2()
{
	int b = 10;
	printf("%p\n", &b);
}
int main()
{
	F1();
	return 0;
}

2.函数调用过程不清晰问题

案例3

这是一个斐波那契契递归Fib

long long Fib(size_t N)
{
	if (N < 3)
		return 1;

	return Fib(N - 1) + Fib(N - 2);
}
int main()
{
	long long n;
	Fib(n);
	return 0;
}

你知道这个递归Fib函数的空间复杂度和时间复杂度吗?
Fib(n)函数每次返回两个函数Fib(n-1)+Fib(n-2).直到n<3时返回1.
也就是Fib函数每次调用都会又调用两个函数,而这个两个函数相当于又调用4个函数依次类推…最后应该调用2^n次
所以时间复杂度为O(2^n).
在这里插入图片描述
那空间复杂度呢?空间复杂度是多少呢?
有的人可能想呀,它不是相当于调用了2 ^ n次嘛,那不就是申请了2 ^ n个空间吗。真的是这样吗?

可能现在还有很多人没有搞清楚函数是怎么调用的,递归是怎么调用的。
有的人可能想是Fib(n)调用Fib(n-1)和Fib(n-2),然后操作系统就给Fib(n-1)和Fib(n-2)分配栈帧了,但其实不是。

函数的调用只有完全调用完才能去执行下一步
调用Fib(n-1)后,其实会继续往下面调用Fib(n-2),然后再往下调用Fib(n-3)直到调用到Fib(2),Fib(2)返回1后,Fib(2)也就结束,函数栈帧销毁,回到F(3),F(3)这时才开始调用F(1),F(1)返回1,F(1)的栈帧销毁,返回F(3),F(2)又开始调用了。依次类推

在这里插入图片描述
Fib(2)返回后,操作系统是不是就将它的空间回收了,然后又调用了Fib(1)所以Fib(1)开辟的空间就是刚刚操作系统收回的空间呀。
其实就是左边Fib(2)和右边F(1)用的是同一块空间。依次类推,Fib(4)返回后,空间被收回,然后操作系统又将空间分配给右边的Fib(3)使用。所以大体上左边和右边是共用一块空间,而左边是调用了n个空间,所以最后的空间大小是O(n).

3.总结

这三个个案例本质上就是要搞清楚函数栈帧是如何创建的以及如何销毁的

搞清楚函数是如何调用的,调用前操作系统要给函数分配栈帧,调用函数结束后,操作系统要将栈帧收回。
如果对函数栈帧方面有兴趣的可以阅读一下博主的《细谈函数栈帧的创建与销毁》。
还有我们可以发现:
时间是一去不复返的,不可再重复利用。
而空间是可以重复利用的。所以我们要特别重视算法的时间效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/363218.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程职场人必备微软出品的实用小工具

系统增强工具PowerToys 下载地址&#xff1a;https://github.com/microsoft/PowerToys 什么是 Windows 系统中&#xff0c;最好的辅助工具&#xff1f; PowerToys 一定可以获得提名。PowerToys 是一款来自微软的系统增强工具&#xff0c;就像是一个神奇的系统外挂&#xff0c;…

精确控制 AI 图像生成的破冰方案,ControlNet 和 T2I-Adapter

ControlNet 和 T2I-Adapter 的突破性在哪里&#xff1f;有什么区别&#xff1f;其它为 T2I 扩散模型施加条件引导的相关研究ControlNet 和 T2I-Adapter 的实际应用效果如何&#xff1f;使用体验上&#xff0c;跟 SD原生支持的 img2img 有什么区别&#xff1f;ControlNet 在插画…

电商数据采集——2022年中国手机行业数据浅析

据国家统计局数据显示&#xff0c;2022年12月&#xff0c;国内手机产量当期值为14310.3万台&#xff0c;同比下降18.4%&#xff1b;累计值为156080万台&#xff0c;同比下降6.2%。 据中国信通院数据显示&#xff0c;2022年12月&#xff0c;国内市场手机出货量2786.0万部&#x…

F.pad() 函数

F.pad() 对tensor 进行扩充的函数。 torch.nn.functional.pad (input, pad, mode‘constant’, value0) input&#xff1a;需要扩充的 tensor&#xff0c;可以是图像数据&#xff0c;亦或是特征矩阵数据&#xff1b;pad&#xff1a;扩充维度&#xff0c;预先定义某维度上的扩充…

内存泄露定位手段(c语言hook malloc相关方式)

如何确定有内存泄露问题&#xff0c;如何定位到内存泄露位置&#xff0c;如何写一个内存泄漏检测工具&#xff1f; 1&#xff1a;概述 内存泄露本质&#xff1a;其实就是申请调用malloc/new&#xff0c;但是释放调用free/delete有遗漏&#xff0c;或者重复释放的问题。 内存…

搞懂事件——C# 的event的机制深度理解

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:无尽的折腾后,终于又回到了起点,工控,我来了 !1. 前言 为什么忽然对Event感兴趣了? 因为进入Web时代以后,很少使用它了,忽然想起这个知识点,…

STM32 CAN波特率计算

STM32 CAN波特率计算简介CAN总线收发&#xff0c;中断方式接收配置代码部分reference简介 CAN通信帧共分为数据帧、远程帧、错误帧、过载帧和帧间隔&#xff0c;本文这里以数据帧为例。 显性电平对应逻辑0&#xff0c;CAN_H和CAN_L之差为2.5V左右。而隐性电平对应逻辑1&#x…

元宇宙对营销方式的影响

营销方式的变化通常伴随着技术的发展。我们已经看到营销方式从印刷媒体、电视、广播到互联网的转变。而现在&#xff0c;我们又处在下一个营销方式大跃进的风口浪尖上。 关于元宇宙及其潜在的变革性影响&#xff0c;人们已经讨论了很多。虽然与元宇宙相关的大多数东西在很大程…

使用 husky 进行基础代码审查

在日常提交 PR 的过程中&#xff0c;我们提交的文件不应该有例如 console、debugger、test.only 等调试语句&#xff0c;这会影响到线上代码。那每次提交之前都检查似乎又像是一个繁琐的工作&#xff0c;如果有个工作能代替我们检查我们提交的代码&#xff0c;让不能提交到线上…

Linux 文件相关操作

文件相关操作 编辑文件 命令&#xff1a; vi 文件名 然后输入i进入编辑模式 编辑完成后输入esc退出编辑 输入:wq保存即便目录下没有这个文件&#xff0c;也可以想使用vi 文件名进行编辑&#xff0c;保存退出后会创建这个文件 查看文件内容 命令&#xff1a; cat 文件名复…

UDP报文详解

目录 &#x1f433;今日良言:走好选择的路&#xff0c;别选择好走的路&#xff0c;你才能拥有真正的自己。 &#x1f43c;一、UDP协议特点 &#x1f43c;二、UDP协议段格式详解 &#x1f433;今日良言:走好选择的路&#xff0c;别选择好走的路&#xff0c;你才能拥有真正的自…

PointNet++训练自己的数据集(附源码)

本文针对PointNet强大的三维点云分类功能&#xff0c;详细讲解怎么训练自己的数据集&#xff0c;在此之前&#xff0c;需要确保已经能够跑通源码的训练和测试&#xff0c;如果没有&#xff0c;请参考PointNet的源码运行。数据放置1.1. 在mytensor_shape_names.txt中配置自己的分…

LeetCode-40.组合总和II

目录题目思路回溯法题目来源 40.组合总和II 题目思路 这道题目和39.组合总和如下区别&#xff1a; 本题candidates 中的每个数字在每个组合中只能使用一次。本题数组candidates的元素是有重复的&#xff0c;而39.组合总和是无重复元素的数组candidates 为了理解去重我们来举…

keepalived+LVS配置详解

keepalivedLVS配置详解keepalived简介keepalived的应用场景keepalived工作原理VRRP协议核心组件分层工作工作状态LVS简介LVS三种模式NAT模式(网络地址映射)IPTUN模式(IP隧道)DR模式(直接路由)三种模式对比keepalivedLVS配置1.master配置2. keepalived配置文件3 修改keepalived配…

做主管如何规范测试团队

当你来到一个项目不规范的技术团队&#xff0c;你会怎么处理呢? 问题 Testing 流程不规范 没有需求评审和设计评审&#xff0c;需求经常是业务或者项目经理直接跟开发提&#xff0c;有时候开发自己都不明白需求&#xff0c;糊里糊涂地就要开发&#xff0c;也没有设计评审&…

插画师培训怎么选,5大插画师培训班排名

插画师培训哪里好&#xff0c;给大家推荐5大插画师培训班排名&#xff0c;各有优势和特色&#xff0c;提供大家选择&#xff01; 一&#xff1a;5大插画师培训班排名 1、轻微课&#xff08;五颗星&#xff09; 主打课程有日系插画、游戏原画、古风插画、动漫漫画&#xff0c;以…

UI自动化测试、接口测试等自动化测试策略

今天跟大家介绍UI测试、接口测试、单元测试主要内容&#xff0c;以及每种测试花费时间讨论。 UI测试【Selenium】 UI测试是最接近软件真实用户使用行为的测试类型。通常是模拟真实用户使用软件的行为&#xff0c;即模拟用户在软件界面上的各种操作&#xff0c;并验证这些操作对…

江苏专转本如何事半功倍的备考

专转本如何事半功倍的备考 一个人学习成绩的优劣取决于他的学习能力&#xff0c;学习能力包括三个要素&#xff1a;规范的学习行为&#xff1b;良好的学习习惯&#xff1b;有效的学习方法。有了规范的学习行为才能培养出良好的学习习惯&#xff0c;形成了良好的学习习惯就会形成…

Android - 代码生成远程依赖库(阿里云)

一、注册 没有注册过阿里云且没有实名认证的点这里&#xff1a;阿里云官网 二、查看库 阿里云制品仓库Packages &#xff08;注&#xff1a;如果没有创建企业或个人使用&#xff0c;按照提示&#xff0c;选个人使用&#xff09; 三、选择类型 选择其中一个&#xff08;两…

问题解决篇 | Win11网络连接上了但是无法上网(修改DNS弹出框框“出现问题”,如何通过网络检测确定并修复网络问题)

目录 问题 网络诊断 Win i 打开设置 搜索“查找并修复网络问题”并点击 "远程计算机或设备将不接受连接" 解决办法&#xff1a; Win R&#xff0c;输入 inetcpl.cpl &#xff0c;点击确定&#xff0c;打开Internet选项 选择“连接” 点击“局域网设置” 三个…