关于堆栈或堆中变量的位置,"Rust Essentials"这本书是否正确?

ean*_*ner 8 heap-memory stack-memory rust

我发现代码来获取存储位置在本书变量锈要点中关于"栈和堆"第2章伊沃Balbaert:

let a = 32;
let mut b = "str";
println!({:p} {:p}, &a, &b);
Run Code Online (Sandbox Code Playgroud)

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

我对这个陈述有一些疑问,因为我听说堆栈地址越来越朝向减少内存地址.上面的第二个地址似乎是堆栈中的一个位置.

我错了吗?

引用:

现在,我们将运行以下程序并尝试可视化程序的内存://参见第2章/ code/references.rs

let health = 32;
let mut game = "Space Invaders";
Run Code Online (Sandbox Code Playgroud)

值存储在内存中,因此它们具有内存地址.运行状况变量包含一个整数值32,它存储在位于0x23fba4的堆栈中,而变量游戏包含一个字符串,该字符串存储在从位置0x23fb90开始的堆中.(这些是我执行程序时的地址,但是在运行程序时它们会有所不同.)

绑定值的变量是指针或对值的引用.他们指向他们; 游戏是对太空入侵者的参考.值的地址由&运算符给出.因此,&health是存储值32的地址,&game是存储Space Invaders值的地址.我们可以使用格式字符串{:p}打印这些地址,如下所示:

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

kaz*_*ase 10

正如用户4815162342评论的那样,这本书是错误的.作为变量的胖指针b就像是一样a.只有它指向的字符串数据才能在其他地方.

在示例中let mut b = "str";,字符串数据实际上不在堆附近.它静态地放在程序的数据段中.要真正将它放在我们需要使用的堆上let b = String::from("str");.生成的内存将如下图所示:

在此输入图像描述

让我们手动检查内存,看看发生了什么.

a并且b位于地址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);
Run Code Online (Sandbox Code Playgroud)

输出看起来像这样:

[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
Run Code Online (Sandbox Code Playgroud)

ab驻留在同一存储区域(0x7F的...),该字符串数据是在不同的区域(0x7e格式...).

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

前三个字节包含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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我以前从未打算过*投票. (3认同)