在 C 中的三元运算符中使用增量

Using increment in ternary operator in C

例如,我使用后

int a = 5, b = 6;
x = (a < b) ? a++ : b++;

x获取a的值,即5,a自增6,符合预期。

当我使用

a = (a < b) ? a++ : b++;

这一行之后,a还是5。

但是

a = (a++ < b++) ? a : b;

a 现在是 6。

为什么会发生这种情况,为什么在第一种情况下不执行增量运算符?

编辑:澄清一下,我问的是为什么当我单独使用这些行时会发生这种情况,一条接一条,而不是同时使用所有三条线。

a = (a < b) ? a++ : b++;

这里,我们把a存入a,然后自增。但它就像

a = a++; // as a<b

显示未定义的行为。

a = (a++ < b++) ? a : b;

这里比较的时候a在自增,所以现在a是6,存入a.

这两种情况都涉及某种未定义的行为,因为您递增 a,返回 a 并在 2 个序列点内分配给左侧的 a。明确定义的结果需要 3 个。

在这种情况下: a = (a < b) ? a++ : b++;

  1. 如果 a 小于 b
  2. a 作为三元运算符
  3. 的结果返回(值 = 5)
  4. a 递增(值 = 6)。
  5. 三元运算符(5)的结果赋值给左侧变量a(覆盖6)

步骤 3 和 4 的顺序未定义。相当于a = a++;

在这种情况下: a = (a++ < b++) ? a : b;

  1. 如果 a 小于 b
  2. ab递增(不管哪个小)
  3. a 作为三元运算符
  4. 的结果返回
  5. 它被分配给左侧变量a

第 2 步和第 3 步的顺序未明确定义。

在这种情况下,跟踪序列点很重要。相关规则:

  • ?左边的第一个三元运算符表达式排在第二个或第三个表达式之前。并且它们中的任何一个在赋值之前被排序。
  • 比较顺序在?
  • 之前
  • 在像 a++ 这样的表达式中,值在递增之前返回

未定义的行为:

  • 在像 a = a++; 这样的表达式中,在 (post) 递增 a 和分配给左侧的 a 之间没有序列点。两者都发生在返回 a 的原始值后
  • 在像 a++ ? a : b 这样的表达式中,在 (post) 递增 a 和从三元运算符返回 a 之间没有序列点。两者都发生在 ?
  • 之后

给你意外结果的一行有一个错误:你不能在同一个表达式中修改一个对象(这里是 a)两次,而不用 "sequence point" 分隔它们。

另一个中的?是一个序列点,所以一个有效。

如果您这样做(在没有序列点的情况下修改两次),您的程序的行为将变得不确定。也就是说,您无法对程序应该产生什么做出任何合理的假设,因此您会看到一些意想不到的结果,这些结果甚至会因编译器及其版本而异。

来自horse's mouth:

6.5 Expressions
...
2 If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.84)
84) This paragraph renders undefined statement expressions such as
    i = ++i + 1;
    a[i++] = i;
while allowing
    i = i + 1;
    a[i] = i;
6.5.15 Conditional operator
...
4 The first operand is evaluated; there is a sequence point between its evaluation and the evaluation of the second or third operand (whichever is evaluated). The second operand is evaluated only if the first compares unequal to 0; the third operand is evaluated only if the first compares equal to 0; the result is the value of the second or third operand (whichever is evaluated), converted to the type described below.110)
110) A conditional expression does not yield an lvalue.
6.5.16 Assignment operators
...
3 An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment,111) but is not an lvalue. The type of an assignment expression is the type the left operand would have after lvalue conversion. The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.
111) The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualified type.

在表达式中

a = (a < b ) ? a++ : b++

(a < b)a++的求值之间有一个序列点,但是在[=17=的LHS上a的求值之间没有序列点] 运算符和 a++;因此,行为未定义。

在表达式中

a = (a++ < b++) ? a : b

?算子右手(a++ < b++)a之间有序列点,但左手a之间没有序列点= 运算符和 (a++ < b++);同样,行为未定义。