基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

C++的野指针(Wild Pointer)和悬空指针(Dangling Pointer)?

知识点图片

在C++中,野指针(Wild Pointer)悬空指针(Dangling Pointer)是两种常见且极其危险的指针错误。它们都会指向无效的内存区域,如果对其进行解引用(Dereference)或修改操作,会导致未定义行为(Undefined Behavior),通常表现为程序崩溃(段错误 Segfault)、数据损坏或安全漏洞。

虽然它们的结果相似,但它们的产生原因完全不同。以下是详细的解析:


一、 野指针(Wild Pointer)

定义:
野指针是指未被初始化的指针。当一个指针变量被声明后,如果没有赋予一个具体的地址,它内部的值是一个随机的内存地址。

产生原因:
仅仅声明了指针变量,但没有对其进行初始化。

代码示例:

cpp
void wildPointerExample() {
    int* p;        // 声明了指针,但未初始化。此时 p 就是一个野指针
    
    // *p = 10;    // 危险!试图向一个随机的内存地址写入数据
                   // 这可能导致程序立刻崩溃,或者静默破坏其他数据
}

二、 悬空指针(Dangling Pointer)

定义:
悬空指针是指曾经指向一段有效的内存,但该内存后来被释放(或销毁)了,而指针本身的指向没有被重置。此时指针仍然保留着原来的地址,但那个地址上的数据已经无效或被分配给其他地方使用了。

产生原因:

  1. 动态内存释放后未置空(使用了 deletefree)。
  2. 超出了局部变量的作用域(例如返回了局部变量的地址)。

代码示例 1:动态内存释放

cpp
void danglingPointerExample1() {
    int* p = new int(5); // 分配有效内存
    delete p;            // 释放内存
    
    // 此时 p 就是一个悬空指针!它仍然存着刚刚释放的内存地址。
    
    // *p = 10;          // 危险!试图修改已经被系统回收的内存
}

代码示例 2:超出变量作用域

cpp
int* getLocalAddress() {
    int a = 10;
    return &a;  // 危险!返回了局部变量的地址
} // 函数结束后,'a' 的内存被系统回收

void danglingPointerExample2() {
    int* p = getLocalAddress(); // p 收到了一个无效的地址,成为悬空指针
    // cout << *p;              // 危险!
}

代码示例 3:块级作用域

cpp
void danglingPointerExample3() {
    int* p = nullptr;
    {
        int a = 5;
        p = &a; // p 指向有效内存
    } // 'a' 的生命周期结束,被销毁
    
    // 此时 p 是悬空指针
    // *p = 10; // 危险!
}

三、 野指针与悬空指针的区别总结

特性 野指针 (Wild Pointer) 悬空指针 (Dangling Pointer)
本质 从未指向过有效的目标。 曾经指向有效目标,但目标已“死亡”。
产生原因 声明指针时未初始化 内存被释放/销毁后,指针未被重置为 nullptr
内部存储的地址 随机的垃圾值。 曾经有效的旧地址。

四、 如何避免这两种指针?(现代C++最佳实践)

在现代C++(C++11及以后)中,遵循以下原则可以几乎完全消灭野指针和悬空指针:

1. 初始化所有指针为 nullptr(防野指针)

声明指针时,如果不立刻分配内存,一定要初始化为 nullptr

cpp
int* p = nullptr; // 安全,试图解引用 nullptr 会稳定触发崩溃,容易调试定位

2. 释放内存后立刻置空(防悬空指针)

cpp
int* p = new int(5);
delete p;
p = nullptr; // 切断联系

3. 绝不返回局部变量的地址或引用(防悬空指针)

如果需要返回数据,直接按值返回(依靠C++的返回值优化 RVO),或者使用动态内存分配(推荐智能指针)。

4. 使用智能指针 (Smart Pointers) —— 终极解决方案

在现代C++中,应尽量避免使用裸指针(Raw Pointer)来管理内存。使用 <memory> 头文件中的智能指针,它们会利用 RAII 机制自动管理内存的生命周期:

  • std::unique_ptr:独占所有权,对象超出作用域自动 delete
  • std::shared_ptr:共享所有权,引用计数为0时自动 delete
  • std::weak_ptr:用于观察 shared_ptr,使用前可以检查对象是否存活(专门用来解决悬空指针和循环引用的问题)。
cpp
#include <memory>

void modernCppWay() {
    // 使用 unique_ptr,不需要手动 delete,也就不会有悬空指针
    std::unique_ptr<int> p = std::make_unique<int>(5); 
    
} // 离开作用域,p 指向的内存自动安全释放
00:00
00:00