【问题标题】:Using increment in ternary operator in C在 C 中的三元运算符中使用增量
【发布时间】:2016-01-20 15:55:00
【问题描述】:

例如,我使用后

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。

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

编辑:为了澄清,我问为什么会发生这种情况,当我分别使用这些行时,一个接一个,而不是同时使用所有三个。

【问题讨论】:

  • 你为什么曾经想做那样的事情?
  • 使用 gcc 4.9.3:a = (a &lt; b) ? a++ : b++; 报告“警告:对 'a' 的操作可能未定义 [-Wsequence-point]”。
  • 更多垃圾代码解释:((

标签: c ternary-operator


【解决方案1】:
a = (a < b) ? a++ : b++;

在这里,我们将 a 存储在 a 中,然后将其递增。不过好像

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

显示未定义的行为。

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

这里,a在比较的时候是递增的,所以现在a是6,存储在a中。

【讨论】:

  • 为什么?你能描述一下吗? @hacks
【解决方案2】:

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

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

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

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

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

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

步骤 2 和 3 的顺序没有明确定义。

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

  • ? 左侧的三元运算符的第一个表达式在第二个或第三个表达式之前排序。并且它们中的任何一个都在分配之前进行了排序。
  • 比较在?之前排序
  • 在像a++ 这样的表达式中,值在递增之前返回

未定义的行为:

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

【讨论】:

  • 为什么这两种情况下递增和赋值的执行顺序不同?
  • 在第一种情况下,a 仍然是 5,它不会增加到 6。
【解决方案3】:

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

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

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

【讨论】:

    【解决方案4】:

    来自horse's mouth

    6.5 表达式
    ...
    2 如果标量对象的副作用相对于不同的副作用是无序的 在同一标量对象或使用同一标量的值的值计算上 对象,行为未定义。如果有多个允许的排序 表达式的子表达式,如果这样一个未排序的一面,则行为未定义 效果发生在任何排序中。84)
    84) 本段呈现未定义的语句表达式,如
        i = ++i + 1;
        a[i++] = i;
    同时允许
        i = i + 1;
        a[i] = i;
    6.5.15 条件运算符
    ...
    4 评估第一个操作数; 在它的求值和 评估第二个或第三个操作数(以评估者为准)。第二个操作数 仅当第一个比较不等于 0 时才评估;仅在以下情况下评估第三个操作数 第一个比较等于 0;结果是第二个或第三个操作数的值 (以评估为准),转换为下述类型。110)
    110) 条件表达式不会产生左值。
    6.5.16 赋值运算符
    ...
    3 赋值运算符将值存储在左操作数指定的对象中。一个 赋值表达式具有赋值后左操作数的值,111) 但不是 一个左值。赋值表达式的类型是左操作数的类型 左值转换后。更新左操作数的存储值的副作用是 在左右操作数的值计算之后排序。 评价 操作数未排序。
    111) 允许实现读取对象以确定值,但不是必需的,即使 当对象具有 volatile 限定类型时。

    在表达式中

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

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

    在表达式中

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

    (a++ &lt; b++)? 运算符右轴上的a 之间有一个序列点,但= 运算符左轴上的a(a++ &lt; b++) 之间没有序列点;同样,行为未定义。

    【讨论】:

      猜你喜欢
      • 2011-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-23
      • 1970-01-01
      • 2016-10-14
      • 2011-02-15
      • 2021-10-25
      相关资源
      最近更新 更多