【问题标题】:Does a function parameter that accepts a string reference point directly to the string variable or the data on the heap in Rust接受字符串引用的函数参数是否直接指向字符串变量或 Rust 堆上的数据
【发布时间】:2020-10-30 09:33:57
【问题描述】:

我从The Rust Book拍摄了这张照片和代码。

为什么s 指向s1 而不仅仅是堆本身的数据?

如果是这样,它是如何工作的? s 如何指向s1。是否使用包含s1 的内存地址的ptr 字段分配了内存。然后,s1 是否又指向数据。

s1 中,我似乎正在查看一个带有指针、长度和容量的变量。只有ptr 字段是这里的实际指针吗?

这是我的第一个系统级语言,所以我认为与 C/C++ 的比较不会帮助我理解这一点。我认为部分问题在于我不太了解指针到底是什么以及操作系统如何分配/取消分配内存。

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

【问题讨论】:

  • 运行此代码时得到的输出是什么?
  • 'hello'的长度是5

标签: pointers rust reference ownership borrowing


【解决方案1】:
  • 内存只是一个巨大的数组,可以通过任何偏移量进行索引(例如u64)。
  • 这个偏移量叫做address
  • 以及一个存储地址的变量,称为指针
  • 但是,通常只有一小部分内存被分配,因此并非每个地址都有意义(或有效)。
  • 分配是对程序有意义的(顺序)地址范围的请求(以便它可以访问/修改)。
  • 每个对象(我指的是任何类型的对象)都位于分配的内存中(因为未分配的内存对程序没有意义)。
  • Reference 实际上是一个指针,它(由编译器)保证是有效的(即从编译器已知的某个对象的地址派生而来)。也可以看看std doc

这里是这些概念的一个示例 (playground):

// This is, in real program, implicitly defined,
// but for the sake of example made explicit.
// If you want to play around with the example,
// don't forget to replace `usize::max_value()`
// with a smaller value.
let memory = [uninitialized::<u8>(); usize::max_value()];

// Every value of `usize` type is valid address.
const SOME_ADDR: usize = 1234usize;

// Any address can be safely binded to a pointer,
// which *may* point to both valid and invalid memory.
let ptr: *const u8 = transmute(SOME_ADDR);

// You find an offset in our memory knowing an address
let other_ptr: *const u8 = memory.as_ptr().add(SOME_ADDR);

// Oversimplified allocation, in real-life OS gives a block of memory.
unsafe { *other_ptr = 15; }

// Now it's *meaningful* (i.e. there's no undefined behavior) to make a reference.
let refr: &u8 = unsafe { &*other_ptr };

我希望澄清大多数事情,但让我们明确地涵盖这些问题。

为什么s 指向s1 而不仅仅是堆本身的数据?

s是一个引用(即有效指针),所以它指向s1的地址。它可能(并且可能)被编译器优化为与s1 相同的内存,逻辑上它仍然是指向s1 的不同对象。

s 如何指向s1。是否使用包含s1 的内存地址的ptr 字段分配了内存。

“指向”链仍然存在,因此调用内部转换为s.deref().lens.len(),并访问转换为s.deref().ptr.add(index).deref() 的字符串数组的某些字节。

图片上显示了 3 个内存块:&amp;s&amp;s1s1.ptr 是不同的(除非优化)内存地址。所有这些都存储在分配的内存中。前两个实际上存储在称为 stack 的预分配(即在调用 main 函数之前)内存中,通常 它不称为分配的内存(我忽略的做法虽然在这个答案中)。相比之下,s1.ptr 指针指向由用户程序显式分配的内存(即在输入main 之后)。

s1 中,我似乎正在查看一个带有指针、长度和容量的变量。只有ptr 字段是这里的实际指针吗?

是的,没错。长度和容量只是常见的无符号整数。

【讨论】:

  • 谢谢。这个解释太棒了。我现在可以看到 s 有自己的内存地址,它的 ptr 值是 s1 中的内存地址,而它又有一个 ptr 字段包含堆上数据的内存地址。
  • 我认为这个解释实际上也帮助我更好地理解了借贷和搬家。如果我取出 println 并对其进行更改,以便此函数 calculate_length 实际上获得值的所有权(移动),我现在看到 s 将是 s1 的浅表副本,包含 ptr、cap、len,指向堆上的数据。这与借用情况下的 s 明显不同,只是指向 s1 的指针。牢记记忆块的视觉效果,我现在可以明白为什么 &T 和 &mut 是它们自己与 T 和 mut T 不同的类型
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多