我在Ivo Balbaert在第2章"堆栈和堆"中的书Rust Essentials中找到了获取变量内存位置的代码:

let a = 32;
let mut b = "str";
println!({:p} {:p}, &a, &b);

这本书的输出是0x23fba4 0x23fb90,它声明第一个地址是堆栈中的位置,第二个地址是堆中的位置.

我对这句话有些怀疑,因为我听说堆栈地址朝着内存地址递减的方向增长.上面的第二个地址似乎是堆栈中的一个位置.

我错了吗?

引述:

现在,我们将运行以下程序,并try 可视化程序的内存:

let health = 32;
let mut game = "Space Invaders";

值存储在内存中,因此它们具有内存地址.这个

值绑定到的变量是指针或引用

println!("address of health-value: {:p}", &health); // prints 0x23fba4
println!("address of game-value: {:p}", &game); // prints 0x23fb90

推荐答案

作为user4815162342 commented,这本书是错误的.变量b的胖指针与a一样位于堆栈上.只有它指向的字符串数据可以在其他地方.

在示例let mut b = "str";中,字符串数据实际上离堆很远.它被静态地放置在程序的data segment中.要真正把它放在堆上,我们需要使用let b = String::from("str");.在下面的图片中,结果如下:

enter image description here

让我们手动判断内存,看看发生了什么.

假设ab位于地址0x7ffeda6df61c和0x7ffeda6df620.

// print part of stack memory starting at &a
let m: &[u8] = unsafe {
    slice::from_raw_parts(&a as *const _ as *const u8, 4 + 16)
};
println!("{:?}", m);

输出将如下所示:

[32, 0, 0, 0, 128, 85, 251, 177, 191, 85, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]

  • 32, 0, 0, 0:a的四个字节
  • 128, 85, 251, 177, 191, 85, 0, 0:b的第一部分,指向字符串数据的64位指针
  • 3, 0, 0, 0, 0, 0, 0, 0:b的第二部分,弦的长度

现在按照数据指针操作:

// manually follow the data pointer
let address = unsafe {
    *(&b as *const _ as *const usize)
};
let p = address as *const u8;
println!("{:p}", p);  // 0x55bfb1fb5580

ab位于同一个内存区域(0x7f…)时,字符串数据位于不同的区域(0x7e…).

// print content of pointer
let s: &[u8] = unsafe {
    slice::from_raw_parts(p, 4)
};
println!("{:?}", s);  // [115, 116, 114, 32]

前三个字节包含s、t和r的ASCII码.第四个字节是任意垃圾.

下面是完整的代码.

use std::slice;

fn main() {
    let a: i32 = 32;
    let b = String::from("str");
    println!("{:p} {:p}", &a, &b);

    // print part of stack memory starting at a
    let m: &[u8] = unsafe {
        slice::from_raw_parts(&a as *const _ as *const u8, 4 + 16)
    };
    println!("{:?}", m);

    // manually follow the str pointer
    let address = unsafe {
        *(&b as *const _ as *const usize)
    };
    let p = address as *const u8;
    println!("{:p}", p);

    // print content of pointer
    let s: &[u8] = unsafe {
        slice::from_raw_parts(p, 4)
    };
    println!("{:?}", s);
}

请注意,该代码示例假定使用64位指针,并依赖于编译器的实现细节,将来或在其他系统上可能会中断.特别是,不能保证堆栈框架或&str的布局.请不要在真正的代码中使用这些选项:)

Rust相关问答推荐

在自身功能上实现类似移动的行为,以允许通过大小的所有者进行呼叫(&;mut;self)?

如何提高自定义迭代器的`extend`性能

如何为rust trait边界指定多种可能性

无法从流中读取Redis请求

如何为 struct 字段设置新值并在Ruust中的可变方法中返回旧值

当发送方分配给静态时,Tokio MPSC关闭通道

如果变量本身不是None,如何返回;如果没有,则返回None&Quot;?

在 Rust 中用问号传播错误时对类型转换的困惑?

RUST 中的读写器锁定模式

Rust 中的静态引用

UnsafeCell:它如何通知 rustc Select 退出基于别名的优化?

Boxing 如何将数据从堆栈移动到堆?

为什么我可以使用 &mut (**ref) 创建两个实时 &mut 到同一个变量?

更新 rust ndarray 中矩阵的一行

Rust Redis 中的 HSET 命令问题

用逗号分隔字符串,但在标记中使用逗号

具有生命周期和以后引用的可变方法

如果我立即等待,为什么 `tokio::spawn` 需要一个 `'static` 生命周期?

在 Rust 中有条件地导入?

Rust:为什么在 struct 中borrow 引用会borrow 整个 struct?