Как прокомментировал user4815162342 , книга не права.Толстый указатель, который является переменной b
, расположен в стеке так же, как a
.Только строковые данные, на которые он указывает, могут быть где-то еще.
В примере let mut b = "str";
строковые данные на самом деле находятся далеко от кучи.Он статически размещен в сегменте данных вашей программы.Чтобы действительно поместить его в кучу, нам нужно использовать let b = String::from("str");
.Полученная память будет выглядеть примерно так, как показано на рисунке ниже:
![enter image description here](https://i.stack.imgur.com/FfVtU.png)
Давайте вручную проверим память, чтобы увидеть, что происходит.
Скажем, 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
не гарантируется.Пожалуйста, не используйте ни один из них в реальном коде:)