gpt4 book ai didi

pointers - 怎么打印!与多个间接级别进行交互?

转载 作者:行者123 更新时间:2023-12-03 11:23:44 24 4
gpt4 key购买 nike

我有以下程序:

fn main() {
let x = 0;

println!("Example 1: {:p}", &x);
println!("Example 1: {:p}", &x);

println!("Example 2: {:p}", &&x);
println!("Example 2: {:p}", &&x);
}

这是一个示例输出:
Example 1: 0x7ffcb4e72144
Example 1: 0x7ffcb4e72144
Example 2: 0x7ffcb4e72238
Example 2: 0x7ffcb4e72290
"Example 1" 的输出始终相同,而 "Example 2" 的那些始终不同。

我已阅读 Does println! borrow or own the variable? ,我从给定的答案中了解到 println!默默引用。换句话说,这听起来像 println!增加了额外的间接级别。

我曾期待 "Example 1" 的输出也要与众不同。看到了 println!默默地采取另一个间接级别, "Example 1"实际上正在使用 &&x , 和 "Example 2"正在与 &&&x 合作.这似乎与我链接的答案一致,特别是: "If you write println!("{}", &x), you are then dealing with two levels of references" .

我认为值 &&x保留将打印为 "Example 1" , 而值 &&&x保留将打印为 "Example 2" .两者 &&x&&&x举行临时 &x ,所以我想 "Example 1"也会打印不同的地址。

我哪里错了?为什么不 "Example 1"打印了不同的地址?

最佳答案

让我们从一个棘手的问题开始:这是否编译?

fn main() {
println!("{:p}", 1i32);
}

我们要求打印 i32作为内存地址。这有意义吗?

不,当然,Rust 理所当然地拒绝了这个程序。

error[E0277]: the trait bound `i32: std::fmt::Pointer` is not satisfied
--> src/main.rs:2:22
|
2 | println!("{:p}", 1i32);
| ^^^^ the trait `std::fmt::Pointer` is not implemented for `i32`
|
= note: required by `std::fmt::Pointer::fmt`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

但是我们知道宏隐式借用了参数,所以 1i32变成 &1i32 .并且引用确实实现了 Pointer .那么有什么关系呢?

首先,它有助于理解为什么宏借用其参数。你有没有注意到 all the formatting traits看起来几乎一模一样?它们都定义了一个方法,名为 fmt ,这需要两个参数, &self和一个 &mut Formatter并返回 Result<(), fmt::Error> .

它是 &self这是相关的。如需调用 fmt ,我们只需要对值的引用,因为格式化值不需要该值的所有权。现在,格式化参数的实现比这更复杂,但最终,对于参数 x ,程序最终会调用 std::fmt::Pointer::fmt(&x, formatter) (对于 :p )。但是,要成功编译此调用,类型为 x必须实现 Pointer ,不是 &x的类型.如 x1i32 ,然后是 x 的类型是 i32 , 和 i32不实现 Pointer .

结论是 :p format 最终将打印由程序中以文本形式编写的表达式所表示的指针的值。在该表达式上进行借用,以便宏不拥有参数的所有权(这对于 :p 仍然有用,例如,如果您想打印 Box<T> )。

现在我们可以继续解释您的程序的行为。 x是局部变量。局部变量通常1有一个稳定的地址2。在您的 Example 1调用,表达式 &x允许我们观察该地址。 &x 的两次出现将给出相同的结果,因为 x在通话之间没有移动。打印的是地址 x (即保存值 0 的地址)。

但是,表达式 &&x有点好奇。取地址两次究竟是什么意思?子表达式 &x产生一个临时值,因为结果没有分配给变量。然后,我们询问该临时值的地址。 Rust is kind enough to let us do that ,但这意味着我们必须将临时值存储在内存中的某个地方,以便它有一些地址。在这里,临时值存储在一些隐藏的局部变量中。

事实证明,在调试版本中,编译器为 &x 中的每一个创建了一个单独的隐藏变量。两次出现 &&x 中的子表达式.这就是为什么我们可以观察到 Example 2 的两个不同的内存地址。线。然而, in release builds ,代码经过优化,只创建了一个隐藏变量(因为在我们需要第二个的时候,我们不再需要第一个,所以我们可以重用它的内存位置),所以两个 Example 2行实际上打印相同的内存地址!

1 我通常这么说是因为在某些情况下优化器可能会决定在内存中移动局部变量。我不知道是否有任何优化器在实践中确实做到了这一点。

2 一些局部变量可能根本没有“地址”!如果从未观察到该变量的地址,优化器可能会决定将局部变量保存在寄存器中。在许多处理器体系结构上,寄存器不能通过指针寻址,因为可以这么说,它们位于不同的“地址空间”中。当然,在这里,我们正在观察地址,因此我们可以非常确信变量实际上存在于堆栈中。

关于pointers - 怎么打印!与多个间接级别进行交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62367385/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com