一招解决大部分的程序崩溃

文摘   2024-10-29 12:06   北京  

你好,我是雨乐~

在C++编程中,作为一个c++开发人员,最怕的是线上出现问题,在各种线上问题中,出现coredump的问题算是最容易解决的,毕竟有现场。而引起coredump的大部分原因则是指针问题,即已经释放的指针重复使用 ,会导致程序崩溃、不可预测的行为,甚至带来安全风险。

今天,借助本文,我们聊聊此种 使用已经释放的指针,即悬空指针

概念

正如在本文一开始我们已经提到了悬空指针,即指向已失效或被释放内存的指针。

当程序尝试访问这样的指针时,会产生不可预知的行为,因为它指向的内存已经不再属于程序,随时可能被其它数据覆盖。

示例如下:

int* fun() {
int x =5;
return&x;// 返回局部变量的地址
}

int main() {
int* ptr =fun();
// ptr 现在是悬空指针
std::cout <<*ptr;// 未定义行为
return0;
}

在这个例子中,fun函数返回了局部变量 x 的地址。但当函数返回后,x 超出了作用域被销毁,导致 main 中的 ptr 成为悬空指针,指向已无效的内存。

常见场景

1.返回局部变量的地址 这是初学者会常犯的错误(甚至在使用某些高级特性后,对高级特性不了解,也容易犯,比如对string_view的使用不当)函数返回后,局部变量被销毁,指针随之悬空。2.使用已删除的指针 如果删除指针指向的内存后仍然访问该指针,则会产生悬空指针。例如:  int* ptr = new int(10);
delete ptr;
*ptr = 20;  // 错误:ptr是悬空指针
3.多个指针指向同一内存 当多个指针指向同一块内存时,释放一个指针后,其他指针会悬空,导致不可预测的行为。4.容器重分配导致的失效引用 在容器操作(如 push_back)触发重新分配时,原有引用可能失效,从而产生悬空指针,这种在面试应届生的时候也可以试试,很多人还是会犯错,比如遍历一个容器,对于元素值等于某个特定值的时候,删除该元素。

风险

悬空指针会带来以下问题:

未定义行为:程序可能崩溃、产生错误结果,或者表现正常,增加问题定位难度。安全漏洞:恶意攻击者可利用悬空指针执行攻击。数据损坏:可能会错误地覆盖程序的其它数据。调试难度大:悬空指针的问题通常不在错误发生点,增加了定位难度。

避免

1.使用智能指针 Modern cpp自C++11引入的智能指针(如 std::unique_ptr)可以自动管理内存,减少手动释放内存的需求。2.RAII(资源获取即初始化) 在对象创建时分配资源,销毁时自动释放。例如,将指针资源封装在类中,类销毁时自动释放内存,确保安全。3.删除后将指针设为 nullptr 当使用原始指针时,释放后将其设为 nullptr,便于在使用前检查指针是否为空。4.使用引用替代指针 如果只需要读取或修改值,使用引用替代指针,避免悬空指针的风险。5.谨慎使用容器迭代器 操作容器时(如添加元素)可能使迭代器失效,应注意避免在操作后继续使用失效迭代器。

检测

尽管我们可以通过各种方法减少悬空指针的产生,实际开发中仍然可能出现。可以使用以下工具检测悬空指针:

Address Sanitizer:快速的内存错误检测器,可以捕捉到释放后使用等错误。Valgrind:内存调试工具,特别是其中的 Memcheck 可以帮助检测内存错误。静态分析工具:如 Clang Static Analyzer 或 PVS-Studio,可以在编译阶段检测悬空指针。

调试

当怀疑有悬空指针时,可以使用调试器设置指针监视点、启用生成coredump、添加日志或assert追踪指针生命周期,也可以结合前述的内存调试工具辅助查找问题。

结语

悬空指针是C++编程中的一个挑战,但通过智能指针、RAII、谨慎管理对象生命周期等方式可以减少悬空指针的发生。随着经验的积累,可以更从容地写出更安全和健壮的C++代码。


以上~~

如果对本文有异议或者有其他技术问题,可以加微信交流:


推荐阅读  点击标题可跳转

1、完美转发不一定完美

2、呃,竟然可以编译通过

3、for循环的演变:从C++03到C++20

雨乐聊编程
毕业于中国科学技术大学,现任某互联网公司高级技术专家一职。本公众号专注于技术交流,分享日常有意思的技术点、线上问题等,欢迎关注