哪些指针值是明确定义的计算?

4

我曾认为,解引用不指向有效对象的指针是未定义行为,但仅仅计算这样的指针是可以的。

然而,如果我正确理解expr.add[4],情况并非如此。

那么,这些指针计算中哪些是明确定义的呢?

int a = 42;
int *p = &a;
p;           // valid, and obviously ok
p++;         // invalid, but ok, because one past the end of 'array' containing 1 element?
p++;         // UB ?

这个案例怎么样?
int *p = nullptr;
p;                // invalid, and obviously ok (considered one past the end?)
p++;              // one past the end? or UB?

除了有效指针外,唯一允许的两个指针是空指针和数组结束后的1个位置。你的链接部分清楚地总结了这一点,是吗? - rustyx
@rustyx 对于第一个情况,我认为已经足够清楚了。但是对于第二个情况,我不太确定。 - cigien
@rustyx 请看链接页面底部的注释(76)。 - Richard Critten
@RichardCritten 这就是我对第一个 p++ 的推理。这个推理难道不适用于第三种情况吗? - cigien
1
你所称之为“无效”的两种情况是有效的。越过末尾和空指针都是有效的指针。(参见[basic.compound]/3) - M.M
显示剩余5条评论
1个回答

5
在您的第一个示例中,第一个 p++ 是明确定义的,因为非数组被视为长度为一的数组。

下面是相关引用 (basic.compound/3.4):

为了指针算术运算 ([expr.add]) 和比较 ([expr.rel], [expr.eq]),数组 x 的 n 个元素中最后一个元素之后的指针被认为等价于指向 x 中假想数组元素 n 和不是数组元素的类型为 T 的对象的指针被认为属于具有 T 类型的一个元素的数组。

执行完 p++ 后,p 将指向 (假想的) 数组的最后一个(且仅有)元素的后面,这是明确定义的。这并不是"无效但可以",因为指向对象结尾之后的指针并不是无效指针,(basic.compound/3.2):

指针类型的每个值都是以下类型之一:

  • [...]

  • 指向对象结尾之后的指针

  • [...]

  • 无效指针值。

在第一个示例中的第二个 p++ 是未定义行为,因为结果将指向假想的(&a)[1]元素之后,这是没有定义的。
在您的第二个示例中,p++ 是未定义行为,因为只有 0 可以添加到 nullptr (expr.add/4.1):

  • 如果 P 评估为空指针值并且 J 评估为 0,结果是一个空指针值。

  • [...]

  • 否则,行为是未定义的


谢谢!非常有用的信息。 - Itachi Uchiwa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接