rust 空悬指针

举报
福州司马懿 发表于 2025/08/07 13:37:55 2025/08/07
【摘要】 在 Rust 中,空悬指针(Dangling Pointer) 是指指向已被释放或无效内存的指针。如果解引用(访问)这样的指针,会导致 未定义行为(Undefined Behavior, UB),可能引发程序崩溃、数据损坏或安全漏洞。Rust 通过其所有权系统(Ownership System) 在编译时完全避免空悬指针,这是 Rust 区别于 C/C++ 等语言的核心安全特性之一。 1. ...

在 Rust 中,空悬指针(Dangling Pointer) 是指指向已被释放或无效内存的指针。如果解引用(访问)这样的指针,会导致 未定义行为(Undefined Behavior, UB),可能引发程序崩溃、数据损坏或安全漏洞。

Rust 通过其所有权系统(Ownership System) 在编译时完全避免空悬指针,这是 Rust 区别于 C/C++ 等语言的核心安全特性之一。


1. 空悬指针的典型场景(在 C/C++ 中)

在 C/C++ 中,空悬指针可能由以下情况导致:

int* dangling_ptr() {
    int x = 10;
    return &x; // 返回局部变量的指针,x 在函数结束后被销毁
}

int main() {
    int* ptr = dangling_ptr(); // ptr 指向无效内存
    printf("%d", *ptr); // 未定义行为!
    return 0;
}

问题x 是栈上的局部变量,函数返回后其内存被释放,ptr 成为空悬指针。


2. Rust 如何防止空悬指针?

Rust 通过 所有权、借用检查和生命周期 确保指针永远有效。

(1) 所有权机制

Rust 要求每个值(内存)必须有唯一所有者,当所有者离开作用域时,内存会被自动释放。

fn no_dangling() {
    let x = 10;
    let ptr = &x; // 借用 x 的引用
    println!("{}", ptr); // 安全:x 的生命周期覆盖 ptr 的使用
} // x 在这里被释放,ptr 不能再使用

关键点

  • ptrx 的引用,但 x 的生命周期必须比 ptr 更长(由编译器检查)。
  • 如果尝试返回 ptr,编译器会报错:
    fn bad_return_ref() -> &i32 {
        let x = 10;
        &x // 错误:x 在函数结束时被释放,返回引用无效
    }
    

(2) 借用检查(Borrow Checker)

Rust 编译器会跟踪引用的生命周期,确保它们不会指向已释放的内存:

fn main() {
    let reference;
    {
        let x = 10;
        reference = &x; // 错误:x 的生命周期不够长
    } // x 在这里被释放
    println!("{}", reference); // 空悬指针!
}

编译器会直接报错,阻止这种危险操作。

(3) 生命周期标注(Lifetime Annotations)

对于复杂情况(如结构体存储引用),需显式标注生命周期:

struct MyStruct<'a> {
    value: &'a i32, // 声明 value 的生命周期与结构体绑定
}

fn main() {
    let x = 10;
    let s = MyStruct { value: &x }; // 安全:x 的生命周期足够长
    println!("{}", s.value);
}

3. 特殊情况:std::ptr::NonNull 和原始指针

Rust 允许使用原始指针(*const T / *mut T,但它们不受所有权系统保护,可能产生空悬指针:

fn raw_pointer_example() {
    let x = 10;
    let ptr = &x as *const i32; // 转换为原始指针(不安全)
    println!("{}", unsafe { *ptr }); // 必须用 unsafe 块解引用
} // x 离开作用域后,ptr 成为空悬指针(但此处未使用,安全)

关键点

  • 原始指针的解引用必须在 unsafe 块中进行。
  • 开发者需手动确保指针有效性(Rust 不保证)。

4. 如何正确处理“类似空悬指针”的情况?

如果需要返回引用,可以使用以下安全模式:

(1) 返回数据本身(所有权转移)

fn no_dangling_return() -> i32 {
    let x = 10;
    x // 所有权转移给调用者,安全
}

(2) 使用 RcArc 管理共享所有权

use std::rc::Rc;

fn shared_ownership() {
    let x = Rc::new(10);
    let ptr = x.clone(); // 引用计数 +1
    // x 和 ptr 可以安全共享数据
}

(3) 使用 Box::leak 故意泄漏内存(极端情况)

fn leak_example() -> &'static i32 {
    let x = Box::new(10);
    Box::leak(x) // 将 Box 转为 'static 生命周期的引用(永不释放)
}

5. 总结

机制 作用
所有权 每个值有唯一所有者,离开作用域时自动释放内存
借用检查 编译时确保引用不会指向已释放内存
生命周期 显式标注引用关系,防止悬垂引用
原始指针(*const T 需手动管理,仅在 unsafe 中使用

Rust 的核心理念
“与其在运行时崩溃,不如在编译时拒绝危险代码。”
因此,Rust 程序不可能出现空悬指针(除非使用 unsafe 绕过检查)。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。