问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

对比C++,Rust在内存安全上做的努力

创作时间:
作者:
@小白创作中心

对比C++,Rust在内存安全上做的努力

引用
CSDN
1.
https://blog.csdn.net/qq_50764711/article/details/143872609

近年来,越来越多的组织建议在系统级开发语言的技术选型中选择内存安全的Rust,而非传统的C/C++。谷歌数据显示,Android的安全漏洞从2019年的223个降低到2022年的85个,这一显著改善主要归功于Rust代码比例的增加。在Android 13中,已有约21%的新原生代码采用Rust开发。微软、AWS和Linux内核等也纷纷拥抱Rust。那么,作为后来者的Rust是如何在内存安全方面做到如此出色的?

悬空指针

悬空指针是C/C++中常见的内存安全问题之一。当一个对象被释放后,如果还有指针指向该对象并尝试解引用,就会导致未定义行为。Rust通过其独特的生命周期机制解决了这个问题。

C++示例

int main()
{
    std::string *ptr = nullptr;
    {
        std::string str;
        ptr = &str;
    }
    printf("%s", ptr->c_str());
    return 0;
}

在这个C++示例中,ptr指向的局部变量str在代码块结束时被释放,但ptr仍然指向已释放的内存,导致未定义行为。

Rust示例

fn main()
{
    let str_ref;
    {
        let str_obj: String = String::new();
        str_ref = &str_obj;
    }
    println!("{str_ref}");
}

在Rust中,编译器会检查引用的生命周期,确保引用不会超出被引用对象的生命周期。如果尝试编译上述代码,Rust会报错:

borrowed value does not live long enough

Rust的生命周期检查器会对比被引用对象和引用之间的生命周期关系,如下图所示:

黄色框表示str_obj的生命周期;引用str_ref的生命周期是从初始化到最后一次使用之间的代码块。生命周期检查器的规则之一是引用的最大生命周期不能超过被引用对象的生命周期,因此上述代码无法通过编译。

缓冲区溢出

在C++中,使用vectordata方法返回原生数组时,如果没有边界检查,很容易发生缓冲区溢出。而在Rust中,通过胖指针机制,可以确保在运行时检测到缓冲区溢出并引发线程panic。

Rust示例

fn main() {
    let vec: Vec<i32> = vec![1,2,3];
    let vec_ref: &[i32] = &vec[0 ..];
    for i in 0 .. 4 {
        println!("{}", vec_ref[i]);
    }
}

这段代码尝试访问超出vec范围的元素,运行时会报错:

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 3', src/main.rs:7:23

这是因为Rust的切片(如vec_ref)是一个胖指针,包含两个机器字:第一个是数据首地址,第二个是数据长度。这使得Rust能够在运行时检测到缓冲区溢出。

对空指针进行解引用

C++中对空指针解引用导致的崩溃问题主要是由于开发人员的疏忽。Rust通过强制要求引用在使用前必须被赋值来避免这个问题。对于可能为空的情况,Rust使用Option类型来显式处理。

Rust示例

fn main() {
    let mut s_ref_option: Option<&String> = None;
    
    let s: String = String::new();
    s_ref_option = Some(&s);
}

要安全地使用Option中的值,可以使用模式匹配:

if let Some(v) = s_ref_option {
    //... v 是&String
}

或者使用unwrap方法,但这种方式需要通过clippy等工具进行代码检查,避免潜在的错误。

非法释放内存

C/C++中存在非法释放内存的情况,如double free或非法释放栈上内存。Rust通过RAII(资源获取即初始化)机制和Drop trait来管理内存资源,使得开发者在Safe Rust中几乎不需要手动管理内存。

Drop Trait示例

pub trait Drop {
    // Required method
    fn drop(&mut self);
}

实现Drop trait的类型实例在被释放前会调用drop方法,这类似于C++的析构函数。

总结

Rust通过其独特的内存安全机制,如生命周期、胖指针、Option类型和RAII,有效地避免了常见的内存安全问题。这些机制使得Rust在系统级开发中越来越受欢迎,成为C/C++的有力替代者。对于开发者来说,掌握Rust不仅能提升代码安全性,还能带来更高效的开发体验。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号