当前位置: 首页 > news >正文

【Rust中级教程】1.4. 内存 Pt.2:栈内存、栈帧(stack frame)、栈指针(stack pointer)

1.4 内存 Pt.2:栈内存、栈帧(stack frame)、栈指针(stack pointer)

1.4.1. 内存区域

程序有很多的内存区域,并不都是在DRAM上的。三个比较重要的区域是栈内存stack、堆内存heap和静态内存staic。

栈内存和堆内存相对比,栈内存更快堆内存更慢。

1.4.2. 栈内存(stack)

有这么一个公理,叫做:“有疑问时,首选Stack”。但是如果想把数据放在栈内存,编译器就必须知道类型的大小。换句话说:“有疑问时,首选实现了Sized的类型”(关于Sized trait的详细内容见【Rust自学】19.5.4. 动态大小和和Sizedtrait)。

Stack是一段内存,程序把它作为一个暂存空间,用于函数调用。

为什么会叫stack呢?因为在stack上的条目是LIFO(Last In First Out,后进先出)

Stack Frame

每个函数被调用,在stack的顶部都会分配一个连续的内存块,叫做stack frame(栈帧)。

在接近stack底部附近是main函数的frame,随着函数的调用,其余的frames都推到了stack上。

函数的frame包含着函数里的所有变量,以及函数所带的参数。当函数返回时,它的frame就被回收了。

构成函数本地变量值的那些字节不会被立即擦除,但访问它们是不安全的,因为它们可能会被后续的函数调用所重写(如果后续函数调用的frame与回收的这个有重合的话)。但即使没有被重写,它们也可能包含无法使用的值。例如函数返回后被移动的值。

Stack Frame也叫activation framesallocation record。只有activation frames被分配在stack上时才叫stack frame。

每个stack frame的大小是不同的。在函数调用期间,stack frame会包含函数的状态。当一个函数在另外一个函数内调用时,原来的函数的值会被及时冻结。

stack fram为函数参数,指向原来调用栈的指针,以及本地变量(不包括在堆内存上分配的数据)提供空间。

stack的主要任务在于为本地变量创造空间,原因在于stack里所有变量都是紧挨着的,找起来更快。

看个例子:

fn main() { let pw = "justok"; let is_string = is_strong(pw); } fn is_strong(password: String) -> bool { password.len() > 5 }

输出:

error[E0308]: mismatched types --> src/main.rs:3:31 | 3 | let is_string = is_strong(pw); | --------- ^^ expected `String`, found `&str` | | | arguments to this function are incorrect | note: function defined here --> src/main.rs:6:4 | 6 | fn is_strong(password: String) -> bool { | ^^^^^^^^^ ---------------- help: try using a conversion method | 3 | let is_string = is_strong(pw.to_string()); | ++++++++++++

问题很明显,pw&str类型的,is_strong函数的参数是String类型。

我们的目标就是让is_strong函数兼容&strString类型。这个目标看起来挺简单但其实有点麻烦:String拥有堆上的缓冲区,而&str只是指向某段UTF-8字节的胖指针,这些字节可能在静态内存、堆上或其他地方,这两个类型的转换不简单。

看看修改后的代码:

fn is_strong<T: AsRef<str>>(password: T) -> bool { password.as_ref().len() > 5 }

这么写就是把传进来的参数作为到str的引用。

也可以这么改:

fn is_strong<T: Into<String>>(password: T) -> bool { password.into().len() > 5 }

这么写就是把传进来的参数转化为String。但是这些写法都涉及到了比较多的转化。

还可以这么写:

use std::fmt::Display; fn is_strong<T: Display>(password: T) -> bool { password.to_string().len() > 5 }

通过to_string方法把参数转化成String类型再操作。这个操作比上一个更慢。

Stack Pointer

随着程序的执行,CPU里有一个游标会随着更新,它反映当前stack frame的当前地址,这个游标就叫做stack pointer(stack指针)。

随着函数内不断地调用函数,stack就会增长(stack pointer从stack frame开始),而stack pointer的值就会减少(越接近stack frame的地方内存地址更大);当函数返回,stack pointer的值会增加(当函数返回时,它的frame就被回收了,游标向stack frame接近,值就变大)。

Stack Frame的消失

stack frames会最终消失的这个事实与Rust生命周期的概念是密切相关的。任何存储在stack上的变量在frame消失后就无法访问了。

所以,任何到stack上的变量的生命周期最多只能与frame的生命周期一样长

http://www.jsqmd.com/news/1256628/

相关文章:

  • 3个维修隐坑|广州打印机打印空白页自查,90%不用更换硒鼓
  • 终极指南:如何用Locale-Emulator免费解决日文游戏乱码问题
  • C#学习进阶
  • maven使用install将jar包编译到本地仓库管理
  • 如何用AI股票分析系统5分钟搭建个人投资决策中心
  • 御坂翻译器:5分钟开启Galgame无障碍体验的终极实时翻译解决方案
  • 御坂翻译器:5分钟开启日系游戏无障碍体验的终极方案
  • Prometheus 监控 Kube-state-metrics 终极实战:Kubernetes 对象状态可观测性全解析
  • 大模型提示工程进阶必修课(语气风格精准锚定技术白皮书)
  • 基于无人机的移动边缘计算网络研究(Matlab代码实现)
  • 济南市历城区阳台防水攻略|2026 封阳台返潮渗漏排查与微创维修方案 - 资讯速览
  • Appium 3.x 实战:元素定位与常见错误解析
  • 九大网盘直链下载助手:完全免费的开源工具终极指南
  • Switch破解终极指南:如何快速配置Atmosphere大气层系统
  • 终极文档下载神器:如何用kill-doc轻松下载任意文档
  • 每日股票分析终极指南:零成本自动化你的投资决策
  • 鸿蒙多功能工具箱开发实战(十六)-实时行情数据获取与展示
  • 语音变压器能做到20Hz到20kHz吗?
  • 如何在5分钟内为AI-Shoujo安装HF补丁:完整游戏增强与模组整合指南
  • 10分钟快速上手:鸣潮智能自动化工具完全指南
  • AMD Ryzen调试神器:SMUDebugTool深度使用与性能优化完全指南
  • 还在用单点AI工具?:真正专业的程序员套装必须满足这5个硬性指标(附性能压测对比数据)
  • Beyond Compare 5企业级激活实战:一键生成永久授权码的完整解决方案
  • 2026终极测评:16款降AIGC工具横评,论文降重降ai率终极答案! - 降AI小能手
  • 单细胞测序数据分析入门:从数据获取到基础分析
  • 如何免费突破百度网盘限速:完整解析工具使用指南
  • 【解决方案】构建零成本AI股票分析系统:GitHub Actions自动化部署实战方案
  • 纯血鸿蒙诞生、大模型降价、《黑神话·悟空》全球成功……属于我们共同的关键瞬间 | 融云 2024 年回顾
  • FreeBSD Podman 安装与 GPT4Free 容器部署指南 by Trae
  • 如何5分钟搞定原神抽卡记录导出:零基础玩家的完整实战指南