Pointers 接受字符串引用的函数参数是否直接指向Rust中的字符串变量或堆上的数据

Pointers 接受字符串引用的函数参数是否直接指向Rust中的字符串变量或堆上的数据,pointers,rust,reference,ownership,borrowing,Pointers,Rust,Reference,Ownership,Borrowing,我拍了这张照片和代码 为什么s指向s1而不仅仅是堆本身上的数据 如果是这样,这就是它的工作原理?s如何指向s1。它是使用包含s1内存地址的ptr字段分配的内存。然后,doess1,依次指向数据 在s1中,我似乎正在查看一个带有指针、长度和容量的变量。这里只有ptr字段是实际指针吗 这是我的第一个系统级语言,所以我不认为与C/C++的比较能帮助我解决这个问题。我认为问题的一部分在于我不太明白指针到底是什么以及操作系统如何分配/释放内存 内存只是一个巨大的数组,可以通过任何偏移量对其进行索引(例

我拍了这张照片和代码

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

如果是这样,这就是它的工作原理?
s
如何指向
s1
。它是使用包含
s1
内存地址的ptr字段分配的内存。然后,does
s1
,依次指向数据

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

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

  • 内存只是一个巨大的数组,可以通过任何偏移量对其进行索引(例如
    u64
  • 此偏移量称为地址
  • 和一个变量,该变量存储一个名为指针的地址
  • 但是,通常只分配了一小部分内存,因此并非每个地址都有意义(或有效)
  • 分配是一种请求,使(顺序)地址范围对程序有意义(以便程序可以访问/修改)
  • 每个对象(我所说的对象是指任何类型的对象)都位于分配的内存中(因为未分配的内存对程序来说毫无意义)
  • 引用实际上是一个(由编译器)保证有效的指针(即从编译器已知的某个对象的地址派生)。再看一看
下面是这些概念的一个示例():

//在实际程序中,这是隐式定义的,
//但为了以身作则,我明确表示。
//如果你想玩这个例子,
//不要忘记替换'usize::max_value()`
//具有较小的值。
让内存=[未初始化::();usize::max_value()];
//“usize”类型的每个值都是有效地址。
const SOME_ADDR:usize=1234usize;
//任何地址都可以安全地绑定到指针,
//它*可能*指向有效内存和无效内存。
让ptr:*常数u8=转化(某些地址);
//你在我们的记忆中找到了一个知道地址的偏移量
让other_ptr:*const u8=memory.as_ptr().add(SOME_ADDR);
//在现实操作系统中,过于简化的分配会产生一块内存。
不安全{*other_ptr=15;}
//现在引用是有意义的(即没有未定义的行为)。
让refr:&u8=unsafe{&*other_ptr};
我希望能澄清大部分问题,但让我们明确地讨论这些问题

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

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

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

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

图片上显示了3个内存块:
&s
&s1
s1。ptr
是不同的(除非经过优化)内存地址。所有这些都存储在分配的内存中。前两个实际上存储在预先分配的内存中(即在调用
main
函数之前),该内存称为stack,通常它不称为分配内存(但在本回答中我忽略了这一做法)。相反,
s1.ptr
指针指向用户程序明确分配的内存(即在输入
main
之后)

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


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

运行此代码时得到的输出是什么?hello的长度是5s。这个解释太棒了。我现在可以看到s有自己的内存地址,它的ptr值是s1中的内存地址,而s1又有一个ptr字段,其中包含堆上数据的内存地址。我认为这个解释实际上也帮助我更好地理解了借用与移动。如果我将println取出并更改它,使这个函数calculate_length实际上拥有值(move),我现在看到s将是s1的浅拷贝,包含ptr、cap、len,指向堆上的数据。这与借来的案例中的s明显不同,只是一个指向s1的指针。记住内存块的视觉效果,我现在可以理解为什么&T和&mut是与T和mut不同的类型
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()
}