You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:48:03