bigywater.tech
Rust · 121

原始指针解引用

裸指针和引用差在哪?什么时候需要它?这篇用小实验把裸指针的安全部分与 unsafe 部分分开验证:创建、比较、算术、别名、Box 往返,最后用它写一个会正确释放的双向链表。

元素为单位
p.add(n) 前进的单位(u32 数组 add(2) 是 8 字节)
E0499 对 0
两个 &mut 与两个 *mut 指向同一个值时的编译错误数
4 个节点
DList 的 Drop 通过 Box::from_raw 释放的节点数

创建与解引用

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      创建裸指针不需要 unsafe;p == q(同一地址): true
      null.is_null() = true  p.is_null() = false
      通过 *mut 修改后 *p = 15
      悬空指针(指向已销毁的 y)创建不报错:地址 0x16f98e80c(解引用它是未定义行为,这里不解引用)

unsafe 的边界见unsafe 的安全边界。

指针算术

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      p.add(2) 指向 30(add 的单位是元素,u32 = 4 字节)
      p.add(2) 比 p 大 8 字节
      byte_add(4) 比 p 大 4 字节
      q - p = 4 个元素(offset_from)
      同样的 add(2),u8 数组:相差 2 字节

引用与裸指针的别名

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      两个 *mut 都指向 x:修改后 x = 12(这里按顺序访问,没有同时读写,结果是确定的)
  源码:
      1 | fn main() { let mut x = 1; let a = &mut x; let b = &mut x; *a += 1; *b += 1; }
  编译:失败
    error[E0499]: cannot borrow `x` as mutable more than once at a time
     --> <实验目录>/src/u121d.rs:1:52
      |
    1 | fn main() { let mut x = 1; let a = &mut x; let b = &mut x; *a += 1; *b += 1; }
      |                                    ------          ^^^^^^  ------- first borrow later used here
      |                                    |               |
      |                                    |               second mutable borrow occurs here
      |                                    first mutable borrow occurs here
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0499`.

Box 与裸指针互转

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      into_raw 得到 0x105769300 —— 所有权交给了裸指针,Box 不会再 drop 它
      from_raw 把 Noisy(2) 的所有权拿回来,离开作用域时正常 drop:
      Noisy(2) drop
      Noisy(1) 从未 from_raw:泄漏,永远不会有它的 drop 输出

Box 的内存布局见Box、Rc、Arc 的内存布局。

裸指针链表

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      正向 [10, 20, 30, 40]  反向 [40, 30, 20, 10]  长度 4
      DList drop:释放了 4 个节点

安全的引用计数方案见Weak<T> 解决循环引用。

分步演示

裸指针
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动
    • 没有验证:裸指针别名的 Stacked/Tree Borrows 规则(需要 Miri)、重复 from_raw 的双重释放后果(UB,不演示)、NonNull 与 PhantomData 的协变与 dropck、悬空指针解引用的实际现象
    京ICP备2026067502号-1京公网安备11010502063487号