回顾:程序的内存分配

程序通过以下两种方式分配内存:

  • 栈:局部变量的连续内存区域。

    • 值在编译时具有已知的固定大小。
    • 速度极快:只需移动一个栈指针。
    • 易于管理:遵循函数调用规则。
    • 优秀的内存局部性。
  • 堆:函数调用之外的值的存储。

    • 值具有动态大小,具体大小需在运行时确定。
    • 比栈稍慢:需要向系统申请空间。
    • 不保证内存局部性。

示例

Creating a String puts fixed-sized metadata on the stack and dynamically sized data, the actual string, on the heap:

  1. fn main() {
  2. let s1 = String::from("Hello");
  3. }

19.1. 回顾:程序的内存分配 - 图1

This slide should take about 5 minutes.

  • 指出 String 底层由 Vec 实现,因此它具有容量和长度,如果值可变,则可以通过在堆上重新分配存储空间进行增长。

  • 如果学员提出相关问题,你可以提及我们不仅能使用[系统分配器]在堆上分配底层内存,还能使用 Allocator API 实现自定义分配器

探索更多

We can inspect the memory layout with unsafe Rust. However, you should point out that this is rightfully unsafe!

  1. fn main() {
  2. let mut s1 = String::from("Hello");
  3. s1.push(' ');
  4. s1.push_str("world");
  5. // DON'T DO THIS AT HOME! For educational purposes only.
  6. // String provides no guarantees about its layout, so this could lead to
  7. // undefined behavior.
  8. unsafe {
  9. let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);
  10. println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}");
  11. }
  12. }