你好,我是雨乐~
在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);
3.多个指针指向同一内存 当多个指针指向同一块内存时,释放一个指针后,其他指针会悬空,导致不可预测的行为。4.容器重分配导致的失效引用 在容器操作(如
delete ptr;
*ptr = 20; // 错误:ptr是悬空指针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++代码。
以上~~
如果对本文有异议或者有其他技术问题,可以加微信交流: