C++ 是一门强大且应用广泛的编程语言,以其灵活性和性能著称。然而,它的一个历史性缺点是缺乏内置的内存安全特性,这可能导致各种与内存相关的缺陷,如缓冲区溢出、悬空指针和内存泄漏。
这是一个众所周知、已持续数十年的问题,人们曾多次尝试解决它。遗憾的是,没有一种尝试提供了完整的解决方案。
这些年来,人们为改善该语言的内存安全做了哪些努力?
垃圾回收器
垃圾回收器(GC)是编程语言和运行时环境中用于自动回收程序不再使用的内存的机制。它的主要目的是管理内存的分配与释放,将开发者从手动内存管理的负担中解放出来,并帮助防止内存泄漏和悬空指针等常见的内存相关错误。
对 C++ 而言,2008 年,对垃圾回收和基于可达性的泄漏检测的最低限度支持被加入了 C++0x。此后,人们提出了许多提案。遗憾的是,没有一个提案能处理所有情况,而垃圾回收器最终在 C++23 中被彻底移除。
借用检查器
借用检查器(borrow checker)是 Rust 编程语言的关键特性,它通过在编译期静态分析程序中引用的所有权和借用关系来强制内存安全并防止数据竞争。这是 Rust 的核心创新之一,也是其所有权模型的基本组成部分。
那么问题来了:为什么不把这一机制引入 C++?
2021 年,谷歌 Chromium 团队试图回答这个问题。在他们的结论论文中,他们得出结论:
乍一看,我们似乎用上面定义的类型成功实现了 C++ 借用检查。遗憾的是,我们并没有。
事实上,正如他们在论文中所描述的,许多挑战仍未解决,借用检查最终无法加入 C++。
运行时检查器
C++ 运行时检查涵盖用于在执行期间验证程序行为和检测错误的各种机制和技术。在 C++ 中,它们被称为检查器(sanitizer)。它们是 Clang 和 GCC 编译器的一部分,提供运行时插桩以在程序执行期间捕获错误。这些检查器通常在开发和测试阶段用于识别和修复问题。然而,它们一般不在生产环境中使用,而生产中出现的许多问题可能在开发期间不会遇到。
即使在开发期间,检查器也没有被普遍采用,因此并不总是被使用。
接下来会怎样?
目前,Stroustrup 正在研究这个安全问题的一个潜在解决方案:它被称为 C++ 配置文件(profiles)——即一组规则,遵循这些规则就能实现特定的安全保证。它们将由 ISO C++ 标准定义,解决指针和数组范围等常见安全问题。
他创建了一个 GitHub 仓库,人们可以在那里提交建议,他也可以分享自己的草稿,从而帮助建立一个社区,在合理的时间框架内推动这项工作向前发展。
结论
C++ 一直在抵制将全面的内存安全引入语言的各种尝试。然而,为这个重大问题找到最终解决方案已变得越来越紧迫。也许我们应该考虑把这个问题加入千禧年大奖难题。:)
