作为user4815162342 commented,这本书是错误的.变量b
的胖指针与a
一样位于堆栈上.只有它指向的字符串数据可以在其他地方.
在示例let mut b = "str";
中,字符串数据实际上离堆很远.它被静态地放置在程序的data segment中.要真正把它放在堆上,我们需要使用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);
输出将如下所示:
[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]
前三个字节包含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
的布局.请不要在真正的代码中使用这些选项:)