C 中 i=i++ 中未定义的行为;但是 i=++i; 呢?

问题描述 投票:0回答:1

我研究了序列点(C99)和之前排序/未排序(C17)以及SO中关于这个主题及其与未定义行为的关系的其他一些帖子。

我认为 C99 非常清楚

i=i++;
i=++i;
都会导致未定义的行为,但根据 C17(甚至 C23),我们有这个:

如果标量对象上的副作用相对于标量对象上的不同副作用是无序的 相同标量对象或使用相同标量对象的值进行值计算,行为 未定义。如果表达式的子表达式有多个允许的排序,则 如果在任何顺序中发生此类未排序的副作用,则行为未定义。

因此,这个:

i=i++;

导致未定义的行为,因为我们在这里对后缀 ++ 运算符和赋值产生副作用,并且根据这篇文章中黑客的回答为什么这些构造使用预增量和后增量未定义的行为?,我们知道两个副作用相对于彼此都是无序的,因此这会导致未定义的行为,但是这个又如何:

i=++i;

至少据我所知,

i
的增量值应该在完整表达式中使用,因此我们再次有两个副作用,但现在预增量运算符的副作用结果必须在副作用结果之前排序赋值运算符,对吗?

现在我的理论是,根据 C11/C17/C23,“i=++i;”是未定义的行为,因为预自增运算符的副作用结果相对于“i”的值计算是无序的在 LHS 对吗?

c language-lawyer sequence c99 c17
1个回答
0
投票

来自 https://port70.net/~nsz/c/c11/n1570.html#6.5.3.1p2https://port70.net/~nsz/c/c99/n1256.html#6.5。 3.1p2

前缀++运算符的操作数的值递增。结果是操作数自增后的新值。表达式 ++E 等价于 (E+=1)。 [...]

来自 https://port70.net/~nsz/c/c11/n1570.html#6.5.16.2p3https://port70.net/~nsz/c/c99/n1256.html#6.5。 16.2p3

E1 op = E2 形式的复合赋值等同于简单赋值表达式 E1 = E1 op (E2),只不过左值 E1 仅计算一次 [...]

所以我们有

i = (i = i + 1);
,其中两个
i
被评估一次。

来自 https://port70.net/~nsz/c/c11/n1570.html#6.5.16p3https://port70.net/~nsz/c/c99/n1256.html#6.5.16p3

赋值运算符将值存储在左操作数指定的对象中。赋值表达式具有赋值后左操作数的值,111),但不是左值。赋值表达式的类型是左操作数在左值转换后将具有的类型。更新左操作数的存储值的副作用在左操作数和右操作数的值计算之后排序。操作数的计算是无序的。

现在我们启动引擎。让我们标记它们

i₁ = (i₂ = i₃ + 1);
只是为了知道我指的是哪个
i

因此分配给

i₂
是在计算
i₃ + 1
之后进行的。

并且分配给

i₁
是在计算
i₂ = i₃ + 1
之后排序的。

但是!分配给

i₁
和分配给
i₂
无序。我们不知道相对于表达式
i₂
的值计算,分配给
i₂ = i₃ + 1
的副作用“何时”会发生。
i₂
可以在之后立即更新,或者可以在下一个序列点处更新。

但是,

i₂ = i₃ + 1
之后没有序列点。在更新
i₁
时更新
i₂
的副作用仍然是“挂”、“待办”。因为更新
i₁
i₂
是无序的,这就是为什么它是未定义的行为。

© www.soinside.com 2019 - 2024. All rights reserved.