【发布时间】:2021-07-27 02:40:17
【问题描述】:
int arr[2];
int b = arr[2];
根据[expr.sub#1]
表达式 E1[E2] 与 *((E1)+(E2)) 相同(根据定义),除了在数组操作数的情况下,如果该操作数是左值且否则为 xvalue。
其中(E1)+(E2) 由 [expr.add#4.2] 确定
当一个整数类型的表达式 J 被添加到一个指针类型的表达式 P 中或从一个表达式 P 中减去时,结果具有 P 的类型。
- [...]
- 否则,如果 P 指向具有 n 个元素 ([dcl.array]) 的数组对象 x 的数组元素 i,则表达式 P + J 和 J + P(其中 J 的值为 j)指向 (如果 0 ≤ i + j ≤ n,则可能是假设的)数组元素 i + j,并且表达式 P - J 指向如果 0 ≤ i - j ≤ n 时 x 的(可能假设的)数组元素 i - j李>
- 否则,行为未定义。
在本例中,arr + 2 指向假设的数组元素,此处没有 UB。 *(arr+2) 的结果是一个泛泛值,它引用了arr + 2 指向的对象。在当前标准中,没有明确规定指针的间接寻址会导致 UB 以及 [basic.life] 的规范性规则。因此,我认为这个例子有两种解释。一个是在该存储中创建了一个 int 类型的实际对象,在这种情况下,int b = arr[2]; 是明确定义的。另一种解释是,根据 [basic.lval#11]
如果程序尝试通过类型与以下类型之一不相似的泛左值访问对象的存储值,则行为未定义:
- 对象的动态类型,
- 对应于对象动态类型的有符号或无符号类型,或
- [...]
在这种情况下,例子被判断为UB。所以,我想知道这个例子肯定是UB还是和我分析的一样(即未指明的情况)?
【问题讨论】:
-
“明确表示指针的间接寻址会导致 UB” - 并非每个表现出未定义行为的程序都必须明确给出未定义行为。相反,每个行为未明确定义的程序都表现出未定义的行为。在这种情况下,您有 UB,因为数组下标的规则专门适用于数组范围内的索引。
-
@user2407038 [expr.sub] 没有指定数组下标值的限制。
-
您询问的是
*(arr + 2),但引用了标准的一部分,它给出了arr + 2的行为。此外,它确实限制了下标的值(“0 ≤ i + j ≤ n”),但在这种情况下,无论如何都满足条件。条件是这样的,因为它想要包含一个过去的指针,但不要将 expr.sub 误认为任何规则,即在arr + 2存在一个对象。 -
你的程序有UB的原因大致是:它要求一个对象存在于
arr + 2。标准中没有规则说那里存在对象。特别是,decl.array 表示““NU 数组”类型的对象由连续分配的 N 类型 U 子对象的非空集合组成,称为数组元素,编号为 0 到 N-1”。 -
从语言律师的角度来看,这确实是一个棘手的问题,因为您必须付出相当大的努力才能确定
arr + 2不引用任何上述数组子对象的事实(并且有很多注释可以非常清楚地说明这一事实,但是,根据您的问题,您似乎只是在寻找基于规范规则的推理)
标签: c++ language-lawyer