【问题标题】:Is checking the value of a dangling pointer safe or Undefined Behavior? [duplicate]检查悬空指针的值是安全的还是未定义的行为? [复制]
【发布时间】:2021-09-30 19:28:01
【问题描述】:

我们只能取消引用一个有效的指针,我们只能检查一个悬空的内置指针指向的地址。我们无法访问它的值(它指向的对象地址中的值)。

int* ptr = nullptr;
if(ptr) // != 0x00000000
   std::cout << *ptr << '\n';

ptr = new int(1000);

if(ptr) // != 0x00000000
   std::cout << *ptr << '\n';

delete ptr; // still pointing at the address of that dynamic object but that object has been destroyed.

if(ptr) // succeeds or undefined behavior?
   std::cout << *ptr << '\n'; // of course UB here

所以对我来说很清楚,但我唯一关心的是检查指针值是安全的还是产生 UB? if(ptr)。因为假设我没有像std::cout &lt;&lt; *ptr 那样访问该地址中的值。

【问题讨论】:

  • 不幸的是,检查指针是否为nullptr 并不能检查指向的对象是否已被销毁。 (显然,如果指针是nullptr,则没有指向对象,但反面不成立。)
  • @fabian:非常感谢!这是我的错,我忘记了指针运算符,
  • 这是自 C++14 以来实现定义的行为,在 C++14 之前未定义:stackoverflow.com/questions/44182049/pointers-in-c-after-delete

标签: c++ pointers dangling-pointer


【解决方案1】:

检查悬空指针的值是安全的还是未定义的行为?

它不是 UB(自 C++14 起),但“安全”取决于您的期望。无法保证此类检查的结果。它可能是真的也可能是假的。假设基于if(ptr) 的指针是有效的通常是不安全的。

【讨论】:

【解决方案2】:

只有当非空指针实际上指向一个有效对象时,才能安全地取消引用它,不幸的是,在 C/C++ 1 中无法测试该条件。 p>

1:除非您手动跟踪有效对象的地址,从而可以在自己的跟踪数据中搜索指向的地址。

测试指针是否等于 null 不是 undefined 行为。然而,根据[basic.stc.general],显然在一块内存被销毁/回收之后,任何引用该块内任何部分的指针值的任何使用都是实现定义的行为:

当一个存储区域的持续时间结束时,表示该存储区域任何部分地址的所有指针的值都变为无效指针值。

间接通过无效指针值并将无效指针值传递给释放函数具有未定义的行为。

对无效指针值的任何其他使用都具有实现定义的行为。

因此,有些人可能会争辩说,这意味着持有已销毁对象地址的指针可能或可能甚至不合法,可以与其他指针甚至nullptr 本身进行比较,因为地址无效。只有编译器才能决定这是否合法。

【讨论】:

  • 没有完美定义,而是实现定义。查看其他答案的评论
  • 检查指针是否等于 null 是一个定义明确的操作。你用那个指针做什么是另一回事。
  • @RemyLebeau 该标准的措辞是“任何其他[解除引用]无效指针值的使用都具有实现定义的行为。”,是否有与 nullptr 比较相关的单独部分与此相矛盾?
  • @Frank if (ptr) 本质上只是if (ptr != 0)if (ptr != nullptr) 的简写。 [conv.ptr] 中的标准说:“空指针常量可以转换为指针类型;结果是该类型的空指针值......相同类型的两个空指针值应该比较相等。” 我读到的意思是如果ptr 有一个空值,那么比较保证为真,否则如果它没有空值,那么比较保证为假。你在看哪个部分?
  • @RemyLebeau [new.delete.single] 用于 delete 呈现指针“无效”,[basic.stc.general] 用于使用实现定义的无效指针(仍然定义明确,只是不便于携带) .
【解决方案3】:

是的,对于int* ptr;,表达式ptr 始终是安全的。程序的行为保持定义。但是

  • 指针的具体值是实现定义的。 [省略号,重点是我的]

[6.8.2.3.4] 指针类型的值是指向或超过对象末尾的指针表示第一个对象的地址 对象占用的内存中的字节(6.7.1)或存储结束后内存中的第一个字节 分别被物体占据。 [...] 指针类型的值表示是实现定义的。指针 与布局兼容的类型应具有相同的值表示和对齐要求(6.7.6)。 [注意:指向过度对齐类型(6.7.6)的指针没有特殊表示,但它们的有效值范围是 受扩展对齐要求的限制。 ——尾注]

  • nullptr 保证等于 (==) 0 和 NULL
  • 在布尔上下文中,nullptr 的计算结果为 false,所有其他值,无论它们是否表示有效对象的地址,计算结果为 true
  • delete 不修改指针。 编辑: 或者可能是这样,请参阅 cmets 中的讨论。
  • 指针具有与整数相同的初始化规则,这意味着本地、非静态成员不会被初始化。 int* ptr; assert(ptr==nullptr); 通常不成立。但是全局指针是零初始化的,因此在开始时评估为 false。

总而言之,虽然程序是安全的,但很容易使它充其量是不确定的。

【讨论】:

  • delete 不修改指针”据我所知是not necessarily true
  • 答案为int ptr;,但代码为int *ptr;。这是故意的吗?
  • @NathanPierson 但是它如何修改指针呢? AFAIK 签名是 void operator delete(void* ptr) - 获取指针的副本,因此它不能修改变量本身,对吧?
  • @chux-ReinstateMonica 不,不是。谢谢,以后请随时修正我的答案中的任何错别字:)
  • @Quimby 我认为这是 delete 表达式和被调用的 operator delete 函数之间的区别。无论如何,如果标准允许编译器这样做,这可能是一个非常学术的观点,但在实践中,不要选择这样做。
猜你喜欢
  • 1970-01-01
  • 2014-11-02
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
  • 2018-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多