什么是悬空指针?

73

我知道这是一个相当常见的问题,但对于我来说仍然很新奇!

我不理解悬空指针的概念,一直在Google上搜索,并编写测试方法来寻找。

我只是想知道这是否是一个悬空指针?因为我找到的所有例子都返回了某个值,而我正在尝试类似的操作!

谢谢!

void foo(const std::string name)
{
    // will it be Dangling pointer?!, with comments/Answer
    // it could be if in new_foo, I store name into Global.
    // Why?! And what is safe then?
    new_foo(name.c_str());
}

void new_foo(const char* name)
{
    // print name or do something with name...   
}

“会成为悬空指针吗?!有评论/答案是” - 不,不会。在您的第一个示例中没有悬空指针。您在那里做的是完全安全和正确的,100%正确。 - Benjamin Lindley
3
没有指针,就不可能有悬空指针。 - gifnoc-gkp
@codemuncher:重新阅读Jack的答案。基本上,只要你不将new_foo用于比foo更全局的地方来存储指针,你就应该是安全的。 - Benjamin Lindley
不会悬空,因为“name”直到foo退出之后才消失,而new_foo退出之后。一个类似的悬挂例子可能是:const char *foo(string name) { return name.c_str(); },因为当foo退出时,“name”消失了。 - JoshG79
1
请注意:我修改了foo的返回类型,以避免因为说你正在做的事情是100%安全和正确而显得愚蠢。 - Benjamin Lindley
显示剩余2条评论
8个回答

89
悬空指针是指向无效数据或不再有效的数据的指针,例如:
Class *object = new Class();
Class *object2 = object;

delete object;
object = nullptr;
// now object2 points to something which is not valid anymore

这甚至可以发生在堆栈分配的对象中:

Object *method() {
  Object object;
  return &object;
}

Object *object2 = method();
// object2 points to an object which has been removed from stack after exiting the function
c_str 返回的指针如果字符串在之后被修改或销毁,则可能变得无效。在您的示例中,似乎没有修改它,但由于不清楚您要如何处理const char *name,因此无法确定代码是否本质上是安全的。
例如,如果您将指针存储在某个位置,然后相应的字符串被销毁,指针将变得无效。如果您仅在new_foo的范围内使用const char *name(例如,用于打印目的),则该指针将保持有效。

2
+(1/2) 对于这个答案来说是正确的。开头的解释很好。最后的建议过于谨慎和不必要。OP的代码是安全的。 - Benjamin Lindley
2
@BenjaminLindley,*...太鲁莽而且不必要的危险行为*。使用strdup来处理std::string就是荒谬的。 - R. Martinho Fernandes
1
@Jack,new_foo怎么可能访问foo的函数参数并修改它呢?OP的例子是安全的。 - Angew is no longer proud of SO
2
@codemuncher:当foo返回时,global_name成为悬空指针,因为指针来源的字符串(在foo中的string name)在那时被销毁了。 - Benjamin Lindley
2
@codemuncher 我不确定你想要“安全”做什么。规则很简单:所有指针必须为null或指向有效的“对象”。(我在使用C++标准中使用“对象”的意义。)当你删除某些东西时,你必须确保(通过设计)不存在指向它的指针。关于std::string::c_str()的返回值:唯一应该使用此函数的时间是与需要char const*的旧函数进行交互时。 - James Kanze
显示剩余16条评论

16
悬空指针是指向未分配(已释放)内存区域的(非NULL)指针。
上面的例子应该是正确的,因为字符串没有通过new_foo进行修改。

@Miklós Homolya,它是“const”..你在new_foo中想修改什么? - code muncher
“const” 的使用是编译器前端的特性,可以通过 const_cast 来克服。问题在于如何以符合标准的方式获取对堆栈变量 “name” 的引用,因为函数 “foo” 的代码已经给出。 - user2545918
我认为 new_foo 无法更改名称。 - Deqing

13

这里摘自这里。虽然是关于C的,但对于C++也是一样的。

悬空指针

当一个指针指向某个变量的内存地址,但在某个时刻该变量已从该内存位置中删除,而指针仍然指向它时,这样的指针称为悬空指针,这个问题称为悬空指针问题。

最初

enter image description here

之后

enter image description here

示例

#include<stdio.h>

int *call();
int main() {

  int *ptr;
  ptr = call();

  fflush(stdin);
  printf("%d", *ptr);
  return 0;
}

int * call() {
  int x=25;
  ++x;

  return &x;
}

输出的结果将是垃圾,因为变量x是一个局部变量。它的作用域和生命周期在函数调用内,因此在返回x变量的地址后,x变量就已经失效了,但指针仍然指向那个位置。


2
“悬空指针”是C语言中最棘手的问题之一,也是C++引入引用的原因之一。不幸的是,引用也可能以同样的方式变成悬空指针。智能指针(https://en.wikipedia.org/wiki/Smart_pointer)是解决这个问题的一种方法。不幸的是,由此产生的行为是未定义的,因此有时结果会变成“垃圾”(如果程序的另一部分使用了该内存区域),有时则不会。这导致了那种难以复制的错误,让人们抓狂。 - Dana
@Yatin,你编辑了一篇引用文献,虽然它不符合引用格式,但是它确实有来源链接,请小心处理。 - anastaciu

9
作为一种风格,我将悬空指针解释为“即使所指向的对象不存在,该指针仍然存在”。
在你的情况下,指针“name”的存在时间比它所指向的对象短。因此,它永远不会成为悬空指针。
在常见的C++类内部,指针在析构函数内部短暂悬挂。这是因为“delete”语句在析构函数的最后一个“}”之前执行,而指针本身在最后一个“}”时停止存在。如果您不想担心这个问题,请使用例如“unique_ptr”。在“unique_ptr::~unique_ptr”析构函数内部,T*指针将短暂悬挂,这是完全安全的。

7
悬垂指针是指在堆栈中有有效指针,但它指向无效的内存。可能会出现这种情况,当你释放堆内存之前未释放堆栈中的指针时。
这是一个安全问题。因为当你释放内存时,我们正在告知操作系统,我们不再需要这部分内存。因此,操作系统会将该内存标记为可分配,并在其他应用程序请求内存时分配给它们。
通常,在C++中,内存通过一般模式分配和释放。当类初始化时,构造函数被调用,这是在堆中分配内存的正确位置。析构函数将在类实例超出作用域时被调用,这是从堆中释放内存的正确位置。假设我们已经创建了一个类,在其中构造函数和析构函数分别进行内存的分配和释放。
int main() {
  SomeClass pointer1 = SomeClass();
  SomeClass pointer2 = pointer1;
}

在上述示例代码中,声明了两个变量,但都保存着相同的值。当构造函数被调用时,它会分配一个堆内存。然后我们再声明一个变量并赋予相同的值。在C++中,通常情况下,当你给复杂类型赋值时,它执行的是浅拷贝(除非你显式实现了复制构造函数),而不是深拷贝。这意味着只有指针在栈中被复制,而堆内存没有被复制。实际上,出于性能原因,不建议复制堆内存。现在最终的内存布局看起来像是有两个指针指向同一块堆内存。

现在当函数执行完成时,局部变量就会超出作用域并调用析构函数。首先,指针2调用析构函数释放堆内存。此时,指针1成为悬空指针。它指向的内存已经被释放。

从这个例子中,我们了解到悬空指针的主要原因是多个所有者共享同一资源。因为当一个指针释放内存时,其他指针也变成了悬空指针。


2

//Declaring two pointer variables to int
int * ptr1;
int * ptr2;
// Allocating dynamic memory in the heap
ptr1 = new int;
ptr2 = ptr1; // Having both pointers to point same dynamic memory location
//deleting the dynamic memory location 
delete ptr1;
ptr1 = nullptr; 
//ptr2 is still pointing the already deleted memory location 
//We call ptr2 is a dangling pointer

0

指针包含一个地址,但该地址不存在或已被删除。

简单来说,指针包含的是一个内存已被删除的位置的地址。

您可以通过一个简单的图示来理解:

Dangling pointer


-2
悬空指针和悬空指针问题 如果任何指针指向某个变量的内存地址,但在某些变量从该内存位置删除后,指针仍然指向该内存位置。
这种指针称为悬空指针,而在此时出现的问题称为悬空指针问题。
以下是一些示例:悬空指针和悬空指针问题

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接