首页 文章

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

提问于
浏览
8

我在第2章关于"Stack and Heap"的Ivo Balbaert书中找到了Rust Essentials一书中变量的内存位置代码:

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

该书的输出为 0x23fba4 0x23fb90 ,它表明第一个地址是堆栈中的位置,第二个地址是堆中的位置 .

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

我错了吗?

引用:

现在,我们将运行以下程序并尝试可视化程序的内存://参见第2章/ code / references.rs让health = 32;
让mut game =“太空侵略者”;
值存储在内存中,因此它们具有内存地址 . 运行状况变量包含一个整数值32,它存储在位于0x23fba4的堆栈中,而变量游戏包含一个字符串,该字符串存储在从位置0x23fb90开始的堆中 . (这些是我执行程序时的地址,但是在运行程序时它们会有所不同 . )绑定值的变量是指针或对值的引用 . 他们指向他们;游戏是对太空入侵者的参考 . 值的地址由&运算符给出 . 因此,&health是存储值32的地址,&game是存储Space Invaders值的地址 . 我们可以使用格式字符串{:p}来打印这些地址,如下所示:println!(“address of health-value:{:p}”,&health); //打印0x23fba4
println!(“游戏 Value 地址:{:p}”,&game); //打印0x23fb90

1 回答

  • 10

    作为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, 0a 的四个字节

    • 128, 85, 251, 177, 191, 85, 0, 0b 的第一部分,64位指向字符串数据的指针

    • 3, 0, 0, 0, 0, 0, 0, 0b 的第二部分,字符串的长度

    现在按照数据指针:

    // 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 的布局 . 请不要在实际代码中使用任何这些:)

相关问题