Правильно ли написано в книге «Rust Essentials» расположение переменных в стеке или в куче? - PullRequest
0 голосов
/ 30 мая 2018

Я нашел код для получения места в памяти для переменных в книге Основы ржавчины Иво Бальберта в главе 2 о "Стек и куча":

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

Книга содержитвыведите 0x23fba4 0x23fb90 и в нем говорится, что первый адрес - это расположение в стеке, а второй - в куче.

У меня есть некоторые сомнения по поводу этого утверждения, поскольку я слышал, что адрес стека увеличивается в сторону уменьшения памятиадрес.Кажется, что второй адрес выше - это место в стеке.

Я ошибаюсь?

Цитата:

Теперь мы запустим следующую программуи попробуйте визуализировать память программы: // см. главу 2 / code / reference.rs

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

Значения хранятся в памяти и поэтому имеют адреса памяти.Переменная здоровья содержит целочисленное значение 32, которое хранится в стеке в расположении 0x23fba4, а переменная game содержит строку, которая хранится в куче, начиная с местоположения 0x23fb90.(Это были адреса, когда я выполнял программу, но они будут отличаться при запуске программы.)

Переменные, к которым привязаны значения, являются указателями или ссылками на значения.Они указывают на них;Игра является ссылкой на Space Invaders.Адрес значения задается оператором &.Итак, & health - это адрес, где хранится значение 32, а & game - это адрес, где хранится значение Space Invaders.Мы можем напечатать эти адреса, используя строку формата {: p} для таких указателей:

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

1 Ответ

0 голосов
/ 30 мая 2018

Как прокомментировал user4815162342 , книга не права.Толстый указатель, который является переменной b, расположен в стеке так же, как a.Только строковые данные, на которые он указывает, могут быть где-то еще.

В примере let mut b = "str"; строковые данные на самом деле находятся далеко от кучи.Он статически размещен в сегменте данных вашей программы.Чтобы действительно поместить его в кучу, нам нужно использовать let b = String::from("str");.Полученная память будет выглядеть примерно так, как показано на рисунке ниже:

enter image description here

Давайте вручную проверим память, чтобы увидеть, что происходит.

Скажем, 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);

Вывод будет выглядеть примерно так:

[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

Пока a и b находятся в одной и той же области памяти (0x7f...), строковые данные находятся в другом регионе (0x7e ...).

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

Первые три байта содержат коды ASCII для s, t и r.Четвертый байт - произвольный мусор.

Ниже приведен полный код.

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 не гарантируется.Пожалуйста, не используйте ни один из них в реальном коде:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...