11.2 原始指针

Rust 中的原始指针(raw pointers)——*const T(不可变原始指针)和 *mut T(可变原始指针)——是底层内存操作的基础工具。与引用(&T&mut T)不同,原始指针不具有所有权语义不受借用检查器约束,且可以为空或悬垂。它们只能在 unsafe 块中解引用,但可以在安全代码中创建和传递。

创建原始指针

原始指针可以从引用、Box 或直接通过地址构造:

let x = 42;
let const_ptr: *const i32 = &x;
let mut y = 100;
let mut_ptr: *mut i32 = &mut y;

// 也可以从整数地址构造(极少使用)
let addr: usize = 0x12345;
let ptr_from_addr = addr as *const u8;

注意:即使从有效引用创建,原始指针本身不保证指向有效内存——一旦原变量离开作用域,指针即变为悬垂。

解引用原始指针

解引用必须在 unsafe 块中进行,且程序员需自行确保安全性:

let x = 5;
let p = &x as *const i32;

unsafe {
    println!("Value: {}", *p); // 安全,因为 x 仍有效
}

p 指向已释放内存、未对齐地址或空指针,解引用将导致未定义行为。

与引用的区别

特性 引用(&T / &mut T 原始指针(*const T / *mut T
所有权 有生命周期,受借用规则约束 无生命周期,无借用检查
空值 不可能为 null 可以为 null
解引用 安全代码中允许 必须在 unsafe 块中
别名规则 遵守唯一可变或多个不可变 无限制,可同时存在多个可变别名
自动解引用 支持 不支持

常见用途

  1. 实现 unsafe 抽象:如 Vec<T>String 等标准库类型内部使用原始指针管理堆内存;
  2. FFI 交互:C 函数常以指针形式传递数据,Rust 需通过原始指针对接;
  3. 系统编程:直接操作硬件寄存器或特定内存地址;
  4. 性能关键路径:绕过借用检查器的某些限制(需极度谨慎)。

例如,手动构建一个切片(类似标准库内部实现):

use std::slice;

let data = vec![1, 2, 3, 4];
let ptr = data.as_ptr();
let len = data.len();

// 安全前提:ptr 有效、对齐、len 不超过分配大小
let my_slice: &[i32];
unsafe {
    my_slice = slice::from_raw_parts(ptr, len);
}
println!("{:?}", my_slice);

这里 slice::from_raw_parts 是一个 unsafe fn,调用者必须保证传入的指针和长度满足切片的安全不变式。

安全注意事项

  • 有效性:指针必须指向有效的、正确类型的内存;
  • 对齐:目标类型有对齐要求(如 u64 通常需 8 字节对齐);
  • 生命周期:所指内存必须在解引用时仍然存活;
  • 并发访问:若多线程访问同一可变原始指针,需自行同步(如使用原子操作或互斥锁)。

小结

原始指针是 Rust 连接安全抽象与底层操作的关键桥梁。虽然它们绕过了语言的安全机制,但通过严格的封装和明确的契约,可以在保持整体程序安全的前提下实现高性能或与外部系统的互操作。正确使用原始指针的核心在于:理解并主动维护那些编译器不再为你检查的不变式

#Rust 入门教程 分享于 1 周前

内容由 AI 创作和分享,仅供参考