C++中的悬挂指针和野指针

悬挂指针(Dangling Pointer), 指的是一个指针它指向已经释放的内存或者无效的内存。当指针指向的内存被释放,这个指针仍然保留着指向之前内存地址的数值,但该地址中的数据已经无效或者被其他数据覆盖

比如一个指针 *Ptr, 它最初指向了一块内存,现在这块内存被释放了,或者这块内存被释放后重新分配给别人,里面是其他的内容。但是这个时候 *Ptr指针依然指向这块内存,

这就会导致问题,它很有可能导致未定义的行为,比如访问无效的内存,程序崩溃或者产生不可预测的结果

 我们来看一个关于悬挂指针的经典的例子:

#include <iostream>

int* CreateInt()
{
int testValue = 1;
int* ptr = &testValue; //指针存储函数内部的局部变量testValue的地址,所以*ptr是一个指向局部变量的指针
return ptr;
}

int main()
{
int* danglingPtr = CreateInt(); //这个时候产生了悬挂指针

//尝试访问悬挂指针指向的内存
std::cout << *danglingPtr << std::end1; //这条语句很有可能导致未定义的未知行为

return 0;
}


 在上面这段代码中, int* danglingPtr = CreateInt();  => CreateInt()方法会返回一个int类型的指针,但是这个指针指向的是CreateInt()方法内部的局部变量testValue. 当我们用CreateInt()执行完这个方法并返回int类型的指针后,它里面的局部变量也就完成了生命周期,内存被释放了。但这个时候,悬挂指针*danglingPtr仍然指向了已经释放了的这块内存的地址。 所以,当我们试图去访问悬挂指针所指向的内存时,就很有可能会产生未定义的行为

为了避免悬挂指针的问题,我们在使用指针时应该注意以下几点:

1.  避免返回指向局部变量的指针

2. 在释放内存后,将指针置为nullPtr, 或者重新为这个指针分配其他有效的内存

3.  在使用指针前,始终都应该检查指针是否为有效指针

上面的例子,返回了指向函数的局部变量的指针,导致了悬挂指针的出现。这其实只是悬挂指针出现的一种情况, 除此之外,还有2种情况,可能会导致悬挂指针的出现

第一种

使用delete语句指针后,该指针没有再次赋值之前,此时这个指针是悬挂指针。 说到这里,我们首先需要理解一下 delete 指针到底是做了些什么

我们知道,在动态分配内存时,我们可以使用new在动态内存中为对象分配空间并返回一个指向该对象的指针, 比如说 int* ptr = new int(1); 为了防止内存耗尽,在这个动态内存使用完毕后,我们必须将其归还给系统,这个时候我们是通过delete语句来讲动态内存归还给系统的 => delete ptr. 那么,我们真正理解了delete ptr是干什么的么

delete ptr => 实际上是删除了ptr所指的目标 ,释放它(ptr所指的目标)所占的堆空间。 而不是删除指针ptr本身 (也就是说,指针ptr本身并没有被删除,它自己仍然存在,自然,该指针所占用的内存空间也并没有被释放)。

所以,delete ptr => 之后, ptr就成了 "空悬指针" => 即ptr此时指向一块已经被回收的内存地址. 这个时候,ptr会在内存里乱指一通,有可能指到一些重要地址造成出错。 为了使用的安全,我们一般在delete ptr之后还会加上 ptr = nullptr; => 这条语句会使其不指向任何的对象。 我们来看一个例子:

#include<iostream>
#include<memory>
using namespace std;

int main(){
  int* ptr = new int(100);
cout << *ptr << end1; //ptr指向的对象
cout << ptr << end1; //ptr指向对象的地址
cout << &ptr << end1; // ptr的地址
delete ptr;

cout << &ptr << end1; //delete后ptr的地址不变
cout << ptr << end1; //delete后ptr指向对象的地址变随机 return 0; }

我们来看看执行的结果

 

 

 第二种  超出变量的作用范围

 

#include<iostream>
using namesapce std;
int main()
{
     int *p;
     {
           int temp = 10;
           p = &temp;

     }

     //p在此处是悬挂指针

    return 0;
}

 

接下来,我们来说说什么是野指针

野指针 =》 野指针是指尚未初始化的指针,也就是说声明了一个指针,但是还没有初始化。 此时它指向的地址是未知的,不确定的,随机的。 我们来看一个例子

#include<iostream>
using namesapce std;
int main()
{
int* p1; //此时声明了,但没有初始化指针p1, 这个时候它就是野指针
cout << "*p1 = " << *p1 << end1; //指针*p1 未初始化就被使用
delete p1;
return 0; }

这样使用时,编译器会直接报错,  产生非法的内存访问

那么如何来避免这种野指针的情况呢,我们可以把指针初始化为NULL,如下

#include<iostream>
using namespace std;
int main()
{
int* p1; //此时p1属于野指针
int* p2 = NULL; // 此时p2不属于野指针,因为给它赋予了初始值NULL
p1 = new int(10); //此时p1就不再是野指针了
p2 = new int(10);
cout << "*p1 = " << *p1 << end1;
cout << "*p2 = " << *p2 << end1;
delete p1;
delete p2;
return 0;

}

 

posted on 2024-04-19 09:53  新西兰程序员  阅读(277)  评论(0编辑  收藏  举报