Rust引用如何存储在内存中(特别是堆栈)

ioekq8ef  于 2023-06-06  发布在  其他
关注(0)|答案(3)|浏览(407)

我一直在学习Rust中的引用,发现很难理解引用是如何存储在堆栈中的。
考虑下面的程序:

fn main() {
    let name = String::from("Somu");
    let name_ref = &name;
}

现在,如何在堆栈帧中为这两个变量分配内存?

|--------------------|
| name     | 0x1fcd  |
|--------------------|
| name_ref | ?       |
|--------------------|

因此,由于String存储在堆中,因此我们将字符串“Somu”所在的堆地址作为变量name的值。
现在,name_ref是对name的引用。在Rust术语中,name_ref借用了name指向的值。
那么,堆栈帧中存储的get是什么,作为name_ref的值?

  • 包含字符串的堆内存的地址?
  • name在堆栈中的地址?

还是别的什么?
有没有人能帮我补充一下这个概念?
先谢谢你了

yrwegjxp

yrwegjxp1#

那么什么get作为name_ref的值存储在堆栈帧中呢?
堆栈中name的地址。
引用是具有编译器在编译期间知道的一些附加属性的指针(可变性,别名,指向现有对象的保证,借用的生命周期等)。但最终它们只是引擎盖下的一个普通的旧指针。它们的大小为usizewide/fat pointers除外)。并保存它们指向的对象的内存地址。
您可以通过使用{:p}打印指针的地址来验证:

fn main() {
    let name = String::from("Somu");
    let name_ref = &name;
    
    println!("{:p}", &name as *const _);
    println!("{:p}", name_ref);
    println!("{:p}", name.as_bytes() as *const _);
}

这可以例如输出:

0x7ffff3dd1350
0x7ffff3dd1350
0x55dfd62769d0

其中,前两个地址是堆栈变量name的地址,第三个地址是String管理的堆分配的地址。

b1payxdu

b1payxdu2#

String可以看作是一个有三个数据成员的结构体。

  • 指向用于构成文本的UTF-8字节的堆分配存储器的指针,
  • 该存储器的容量的整数,
  • 一个整数,表示该存储中实际使用的字节数(<=capacity,用于有效的调整大小策略)。

当我们说一个String是堆分配的,它只是一个强调文本(utf-8字节序列)是堆分配的事实的捷径;结构体本身可能是堆栈分配的(取决于您的代码)。
在您的示例中,nameString(结构体)是堆栈分配的,作为任何局部变量,尽管它包含的文本可能是堆分配的,并且name_ref仅包含此结构体在堆栈上的地址(它指向堆栈上非常接近的地址)。
请注意,这种描述代码功能的方式是对非优化编译器(如调试模式)功能的简化,其中每个变量都位于内存中并具有地址。当涉及到优化编译器时(如在发布模式下),许多变量并不简单地存在(在内存中有一个专用的存储),它们被重构为一系列对寄存器的等效操作和很少的内存访问。

6pp0gazn

6pp0gazn3#

它是对存储在堆栈上的String值的引用,这意味着该引用将存储指向该值的指针。引用将不引用堆;该分配在String内部。
然而,在编译器优化的情况下,很可能会省略这种引用。
同样值得注意的是,deref强制允许从&String创建&str绑定,在这种情况下,&str * 将 * 指向非空String的堆。

相关问题