【问题标题】:What is a dangling pointer?什么是悬空指针?
【发布时间】:2021-12-18 19:11:11
【问题描述】:

我知道这是一个很常见的问题,但对我来说还是个新问题!

我不明白悬空指针的概念,正在谷歌搜索,并编写测试方法以找到一个。

我只是想知道这是一个悬空指针吗?由于我发现的任何示例都返回了一些东西,所以我正在尝试类似的东西!

谢谢!

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...   
}

【问题讨论】:

  • "会是悬空指针吗?!用 cmets/Answer 是" -- 不,不是。您的第一个示例中没有悬空指针。您在那里所做的一切都是完美的、100% 安全和正确的。
  • 我不明白你怎么能有一个 悬空指针 ...没有 指针
  • @TheOtherGuy 我的意思是,没有 const char* ... 携带相同的 const std::string
  • @BenjaminLindley 好吧,形成 Jack 的回答,这似乎是真的,因为 c_str() 返回指向字符串的指针,这在 new_foo 中可能无效 .... 可能是错误的!
  • 注意:我编辑了 foo 的返回类型,以免因为说你所做的是 100% 安全和正确而显得愚蠢。

标签: c++ pointers dangling-pointer


【解决方案1】:

悬空指针是指向无效数据或不再有效数据的指针,例如:

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(例如,用于打印目的),那么指针将保持有效。

【讨论】:

  • +(1/2) 这个答案。开头的解释很好。最后的建议是偏执且不必要的。 OP 的代码是安全的。
  • @BenjaminLindley ...鲁莽和不必要的不​​安全。在std::stringstrdup 之间工作简直太荒谬了。
  • @Jack new_foo 怎么能访问foo 的函数参数来修改它? OP 的例子是安全的。
  • @codemuncher: 当foo 返回时,global_name 变成了一个悬空指针,因为该指针所源自的字符串(foo 中的string name)在那个时候被销毁了。
  • @codemuncher 我不确定你想“安全地”做什么。规则很简单:所有指针必须为空或指向有效的“对象”。 (我使用 C++ 标准中使用的“对象”。)当您删除某些内容时,您必须确保(通过设计)没有指向它的指针继续存在。关于std::string::c_str() 的返回值:唯一 应该使用此函数的时间是与需要char const* 的旧函数交互时。
【解决方案2】:

悬空指针是指向未分配(已释放)内存区域的(非 NULL)指针。

上面的例子应该是正确的,因为字符串不是通过new_foo修改的。

【讨论】:

  • @Miklós Homolya 它的“const” .. 在 new_foo 中修改是什么意思?!
  • “const”的使用是(编译器)前端功能,可以通过 const_cast 来克服。问题是如何以符合标准的方式获得对堆栈变量“name”的引用,因为函数“foo”的代码已经给出。
  • 我觉得new_foo不能改名
【解决方案3】:

取自here。虽然,即使是 C 语言,C++ 也一样。

悬空指针

当一个指针指向一个变量的内存地址,但经过一段时间后,该变量从该内存位置删除,而指针仍然指向它,那么这样的指针被称为悬空指针,这个问题是称为悬空指针问题。

最初

稍后

示例

#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 变得死了,并且指针仍然指向该位置。

【讨论】:

  • “悬空指针”是 C 语言中最有害的问题之一,也是 C++ 创建引用的原因之一。可悲的是,引用也可能以同样的方式变得悬空。智能指针 (en.wikipedia.org/wiki/Smart_pointer) 是解决问题的一种方法。不幸的是,随后的行为是未定义的,因此有时结果会变成“垃圾”(如果程序的另一部分使用该内存区域),有时则不会。这导致了一种难以复制的错误,使人们扯掉了头发。
  • @Yatin,你编辑了一个引文,虽然它不是引文格式,但它确实有链接,请小心。
【解决方案4】:

作为一种风格,我将悬空指针解释为“一个仍然存在的指针,即使它指向的对象不再存在”。

在您的情况下,指针 name 存在的时间比它指向的对象的时间短。所以它永远不会悬空。

在常见的 C++ 类中,指针在析构函数中的悬浮时间很短。这是因为delete 语句在析构函数的最后一个} 之前,而指针本身在最后一个} 处不再存在。如果您不想担心这一点,请使用例如unique_ptr&lt;T&gt;T* 指针将在 unique_ptr::~unique_ptr 析构函数中悬空很短的时间,这是非常安全的。

【讨论】:

    【解决方案5】:

    悬空指针是指堆栈中有有效指针但指向无效内存的情况。当您在堆栈中的指针被释放之前释放堆内存时,您可能最终会遇到这种情况。

    这是一个安全问题。因为当您释放内存时,我们会通知操作系统,我们不再需要这部分内存。因此,当其他应用程序请求内存时,操作系统会将那块内存标记为准备好分配并分配给其他应用程序。

    通常,在 C++ 中,通过通用模式分配和释放内存。当类初始化时调用类中的构造函数,这是在堆中分配内存的正确位置。当类实例超出范围时将调用析构函数,这是从堆中释放内存的正确位置。假设我们已经创建了一个类,分别在构造函数和析构函数中分配和释放内存。

    int main() {
      SomeClass pointer1 = SomeClass();
      SomeClass pointer2 = pointer1;
    }
    

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

    现在,当函数执行完毕后,局部变量会超出范围并调用析构函数。首先,pointer2 调用释放堆内存的析构函数。此时,pointer1 变成了悬空指针。它指向一个已经被释放的内存。

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

    【讨论】:

      【解决方案6】:
      
      //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
      

      【讨论】:

        【解决方案7】:

        悬空指针和悬空指针问题 如果任何指针指向任何变量的内存地址,但在某个变量从该内存位置删除之后,而指针仍指向该内存位置。

        该指针称为悬空指针,此时出现的问题称为悬空指针问题。

        这里有一些例子:Dangling Pointer and dangling pointer problem

        【讨论】: