标准中关于超出范围指针的未定义行为的歧义

Ambiguity in the standard on undefined behaviour of out of range pointer

ISO IEC 14882-2011 §5.7/5 状态:

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

本节不时在Whosebug上使用。例如争论为什么指向 nullptr 的指针的增量是 UB 就像 。然后它被解释为,有一个不指向数组对象元素的指针。是未定义的行为。

然而,当我读到这篇文章时,我理解它是指指针的评估是 UB。这意味着拥有这样一个指针是明确定义的行为。当一个人试图取消引用它时,行为变得不确定。

这意味着,例如,将有效指针递增到数组边界之外是合法的。之后再次递减它是合法的。并且由于指针将与增量之前的值相同,因此评估也是合法的。

两者是哪个?

"The evaluation"表示加法运算的评价;因此 UB 不会在非评估上下文(sizeofdecltype 等)中发生(比如)static_cast<int*>(nullptr) + 1

不代表"the evaluation of the pointer",当然也不是解引用;如果该标准打算进行这种解释,它就会这样说。

递增然后递减空指针仍然是未定义的行为。当 UB 发生时,任何事情都可能发生,因此这将是一个有效的事件序列:

  1. 增加空指针。未定义的行为,所以我们将指针设置为 0xDEADBEEF 因为我们可以。
  2. 递减指针。也是未定义的行为,除非 0xDEADBEEF 恰好在第一个元素之后的有效数组中。
  3. 取消引用指针。发鼻魔。

您引用的段落指的是指针运算,而不是指针求值。

表示定义的唯一时间指针加法p + i是if
(将 i 的减法等同于 -i 的加法)

  1. p 指向数组对象的一个​​元素或最后一个元素之后的一个元素,并且
  2. p + i 指向同一个数组对象的一个​​元素,或者指向最后一个元素的后一个

如果 p 不是 指向数组元素的指针或 "one past the end" - 例如,如果它是空指针或 "two past the end" - 行为未定义。
您不需要取消引用结果来导致未定义的行为 - 添加本身的效果是未定义的。

也就是说

int p[1] = {0};
int *q = p;  // OK
q = q + 1;   // OK - one past the end
int *r = q + 1;   // Undefined behaviour
r = r - 1;   // Doesn't make r valid or the program un-undefined

同样

int *p = nullptr;
p++; // Undefined
p--; // Still undefined