【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 frames或allocation 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函数兼容str和String类型。这个目标看起来挺简单但其实有点麻烦String拥有堆上的缓冲区而str只是指向某段UTF-8字节的胖指针这些字节可能在静态内存、堆上或其他地方这两个类型的转换不简单。看看修改后的代码fn is_strongT: AsRefstr(password: T) - bool { password.as_ref().len() 5 }这么写就是把传进来的参数作为到str的引用。也可以这么改fn is_strongT: IntoString(password: T) - bool { password.into().len() 5 }这么写就是把传进来的参数转化为String。但是这些写法都涉及到了比较多的转化。还可以这么写use std::fmt::Display; fn is_strongT: Display(password: T) - bool { password.to_string().len() 5 }通过to_string方法把参数转化成String类型再操作。这个操作比上一个更慢。Stack Pointer随着程序的执行CPU里有一个游标会随着更新它反映当前stack frame的当前地址这个游标就叫做stack pointerstack指针。随着函数内不断地调用函数stack就会增长stack pointer从stack frame开始而stack pointer的值就会减少越接近stack frame的地方内存地址更大当函数返回stack pointer的值会增加当函数返回时它的frame就被回收了游标向stack frame接近值就变大。Stack Frame的消失stack frames会最终消失的这个事实与Rust生命周期的概念是密切相关的。任何存储在stack上的变量在frame消失后就无法访问了。所以任何到stack上的变量的生命周期最多只能与frame的生命周期一样长。
