【发布时间】:2018-11-05 21:00:19
【问题描述】:
简而言之,以下代码是否被认为具有未定义的行为?
int main()
{
int *p = <some invalid pointer value>;
}
编译示例,代码如下:
int main()
{
int *p = new int;
delete p; // Now p has an invalid pointer value.
int *q = p; // UB?
}
我已经对该主题进行了一些研究,所以这些是我目前找到的相关信息:
指针值(根据cppreference)可以是以下之一:
- 指向对象或函数的指针;
- 超过对象末尾的指针;
- 空指针值;
- 一个无效的指针值。
另外,根据 cppreference,
通过无效指针值的间接传递以及将无效指针值传递给释放函数具有未定义的行为。对无效指针值的任何其他使用都具有实现定义的行为。
This thread 解决了无效指针的一些用途。具体来说,this answer 提到了Rationale document (C99),其中包含以下段落(第 6.3.2.3 节):
不管无效指针是如何创建的,对它的任何使用都会产生未定义的行为。即使赋值、与空指针常量比较或与自身比较,在某些系统上也可能导致异常。
我不确定 C++ 的情况如何,但我认为,鉴于链接线程上的答案,使用 无效指针会导致未定义的行为。但请注意,assignment 与 initialization 不同,所以我不确定初始化是否被视为一种用途。
【问题讨论】:
-
@super "只要你不尝试取消引用就可以了。" 这是错误的。请参阅 OP 提供的标准引用。
-
不寻找语言标准,但将指针加载到 68000 address register 或 x86 segment register 将验证地址。如果它当前没有映射到 RAM,它会陷入陷阱。
-
那只证明实现定义。顺便说一句,如果指针永远不会被取消引用,那么人们会期望实现不会加载到这些寄存器中。
-
@rustyx - 只是指出复制无效指针未明确定义的原因。编写语言标准的人很清楚这一点。如果我曾经为 68000 编写过编译器,我肯定会使用专用地址寄存器来复制指针。为什么要为此浪费数据寄存器?
-
@BoPersson 68000 仅在您尝试取消引用时检查存储在地址寄存器中的值的有效性。这些寄存器只是“特殊”的,因为它们可以用于间接指令,而数据寄存器不能。虽然您是正确的,加载具有无效值的段寄存器会导致 x86 中的异常,但编译器只会在要取消引用指针时发出加载此类寄存器的代码。此操作代价高昂,因此编译器不会执行此操作,除非必须这样做。
标签: c++ language-lawyer undefined-behavior invalid-pointer