【问题标题】:C++: Why will this not cause a dangling pointer?C++:为什么这不会导致悬空指针?
【发布时间】:2021-12-18 09:29:31
【问题描述】:

这是一个悬空指针

Foo* p;
{
  Foo f;
  p = &f;
}
// now p is dangling pointer

但是,为什么这不是一个悬空指针?

Foo* p;
{
  Foo f;
  ...
  *p = f;
}
// p still contains the data of f

是不是因为在第二种情况下,我们在做副本?这是否意味着我应该写

*p = std::move(f);

如果我不想要副本?

谢谢!

【问题讨论】:

  • 这是 ub。指针 p 未初始化。
  • 您正在取消引用一个未初始化的指针。这与你无关吗?尝试复制到 p 指向的地方,但 p 指向哪里?
  • 你的第一个例子产生一个悬空指针,你的第二个例子可以有效地被视为已经是一个悬空指针。尽管它在技术上不符合“悬空”的定义,但它同样无法使用。
  • 在第二个花瓶中,p 是一个未初始化的指针,因此访问它的值(更不用说通过*p 取消引用它)会产生未定义的行为。要对*p 执行任何操作而不调用未定义的行为,必须初始化或分配p,使其指向一个有效对象(例如p = &some_object_that_exists; *p = f;

标签: c++ pointers


【解决方案1】:

在您的第二个示例中,p 未初始化。取消引用未初始化的指针是未定义的行为。

【讨论】:

  • 确实如此,但如果问题是“为什么这不会导致悬空指针?”,这能回答这个问题吗?
  • @DrewDormann 这个问题没有实际意义,因为当代码出现未定义行为时,您不能谈论指针是否悬空,因为在使用之前没有初始化相同的指针。
  • 我同意你的看法。这个问题没有实际意义。
【解决方案2】:

首先,通过给 p 一个有效值来明确定义行为(不要让事物未初始化是最简单、最便宜的错误预防形式):

Foo* p = new Foo;
{
    ...

Foo x;
Foo* p = &x;
{
    ...

现在,在您的第一段代码中,p 存储了已被销毁的 f 的位置。
这就是使它成为悬空指针的原因。

在第二个中,您不更改p,而是将f 存储在*p 中,这与以前一样是同一个对象。
由于该对象仍然存在,因此指针不会悬空。

比较这两个sn-ps:

int x = 0;
int* p = &x;
{
    int y = 1;
    p = &y;
}
std::cout << "p: " << p << ", &x: " << &x;

int x = 0;
int* p = &x;
{
    int y = 1;
    *p = y;
}
std::cout << "p: " << p << ", &x: " << &x;

你会看到第一个打印了两个不同的地址,第二个打印了两次相同的地址。

【讨论】:

    【解决方案3】:

    区别(如果有的话)取决于您对“悬空指针”的定义。而且很学术。

    讨论一种可能的定义。悬空指针的一种可能定义是“指向不再有效数据的指针”。

    根据这个定义,你的第一个例子;

    Foo* p;
    {
      Foo f;
      p = &f;
    }
    // now p is dangling pointer
    

    导致p 是一个悬空指针,因为它指向对象f,并且f 在其封闭范围的末尾不再存在(即})。由于p仍然存在,并且在f不存在的过程中它的值没有改变,所以p现在指向一个不再存在的对象。

    根据同样的定义,你的第二个例子;

    Foo* p;
    {
      Foo f;
      ...  
      *p = f;
    }
    // p still contains the data of f
    

    不会产生悬空指针,因为p 从未指向有效对象。事实上,p 是未初始化的,所以简单地访问它的值会产生未定义的行为,并且它的值(如果有的话)是不确定的。取消引用(即评估 *p)不能在不评估 p 的情况下完成,因此表达式 *p 和语句 *p = f 都给出了未定义的行为。在} 之后对*p 执行任何操作会导致未定义的行为(就像在您的第一个示例中所做的那样)。

    有了这个定义,p 在第一个例子中是一个悬空指针而不是在第二个例子中的区别有点学术。不同之处在于,在第一种情况下,p 被视为悬空指针,因为它不再指向有效对象,但在第二种情况下,p 不被视为悬空指针因为它从不指向一个有效的对象。实际上,这种区别没有任何区别,因为使用 *p 做任何事情都会产生未定义的行为。

    第二个可能定义的讨论 悬空指针的第二个可能定义与第一个“指向无效数据的指针”略有不同。

    根据第二个定义,您的两个示例都会产生一个悬空指针。第一个导致p 指向一个不再存在的对象——因此它指向无效数据。在第二个示例中,p 从未指向有效对象 - 因此它也指向无效数据。根据悬空指针的定义,p 在两个示例中都是在 } 之后的悬空指针,因为它现在指向无效对象。

    第二个定义下的区别仍然是学术性的。简单地评估 *p(更不用说使用它来更改无效对象 p 指向,例如 *p = some_Foo)会给出未定义的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-10
      • 1970-01-01
      • 1970-01-01
      • 2020-09-07
      • 2021-01-03
      • 2018-12-20
      • 2015-07-04
      • 1970-01-01
      相关资源
      最近更新 更多