Rust从裸指针加载值得到错误结果的问题咨询
问题成因确认
你的猜测完全正确,核心问题有两个:
get_pointer函数的参数a是按值传递,当你把测试函数里的变量a=5传入get_pointer时,会在get_pointer的栈帧上复制生成一个临时变量,ptr::addr_of!(a)拿到的是这个临时变量的栈地址。函数执行完成后栈帧回收,该地址对应的内存被标记为可用,后续访问属于未定义行为,读到0是栈内存被回收后的典型结果。- 额外注意:即使你修复了临时变量问题,把指针序列化为
usize再反序列化回指针的操作,仅在同一进程内、指针对应的内存未被释放/移动的场景下有效,跨进程、程序重启后这个操作完全无效,因为虚拟地址空间是每个进程独立的。
正确实现示例
方案1:直接在当前作用域获取指针,避免跨函数取临时变量地址
#[cfg(test)] mod tests { use std::ptr; use bincode; #[test] fn f() { unsafe { let a = 5; // 直接在a的作用域内取指针,指向的a直到测试函数结束才会销毁 let pointer = ptr::addr_of!(a); let encoded = bincode::serialize(&(pointer as usize)).unwrap(); let decoded = bincode::deserialize::<usize>(&encoded[..]).unwrap() as *const i32; let b = std::ptr::read(decoded); assert_eq!(a, b); } } }
方案2:如果需要封装获取指针的函数,改为传引用获取原变量地址
#[cfg(test)] mod tests { use std::ptr; use bincode; // 参数改为不可变引用,拿到的是原变量的地址,不是临时变量的 fn get_pointer<T>(a: &T) -> *const T { ptr::addr_of!(*a) } #[test] fn f() { unsafe { let a = 5; let pointer = get_pointer(&a); let encoded = bincode::serialize(&(pointer as usize)).unwrap(); let decoded = bincode::deserialize::<usize>(&encoded[..]).unwrap() as *const i32; let b = std::ptr::read(decoded); assert_eq!(a, b); } } }
额外说明
如果你的最终需求是序列化数据本身而非指针,直接序列化变量即可,不需要绕指针转换,这才是跨场景通用的序列化方案:
let encoded = bincode::serialize(&a).unwrap(); let b: i32 = bincode::deserialize(&encoded[..]).unwrap(); assert_eq!(a, b);
所有对裸指针的读写操作都属于unsafe范畴,你必须自行保证指针指向的内存是有效的,否则一定会触发未定义行为。
内容的提问来源于stack exchange,提问作者gregor45
相关产品推荐
相关产品推荐

