【问题标题】:Logical operators' precedence in C [duplicate]C中逻辑运算符的优先级
【发布时间】:2018-12-15 05:26:25
【问题描述】:
#include <stdio.h>

int main(void) {
    int a = 0, b = 0, c = 0;
    ++a || ++b && ++c;
    printf("%d %d %d", a, b, c);
    return 0;
}

gcc 8.1.0 的输出为 100&amp;&amp; 的优先级应该高于 ||

为什么bc 仍然是0

【问题讨论】:

  • 因为短路

标签: c


【解决方案1】:

表达式++a || ++b &amp;&amp; ++c分组++a || (++b &amp;&amp; ++c)但是|| 的右侧仅在 ++a0 时才被评估,而事实并非如此。

【讨论】:

  • 括号里的表达式不是要在“||”之前计算吗?
  • @EricShi 绝对不是|| 的左侧首先被评估。
  • @EricShi 您将优先级与评估顺序混淆了......
  • @EugeneSh。非常重要。
  • @EricShi,它应该在||之前被评估,但++a不是||,它是它的一个操作数。
【解决方案2】:

这里有三个问题:

  1. 优先顺序。
  2. 评估顺序。
  3. 逻辑运算符的短路。

优先顺序意味着++a || ++b &amp;&amp; ++c 被评估为++a || (++b &amp;&amp; ++c)

但是,由于逻辑运算符的短路要求,首先评估 ++a。仅当计算结果为 false 时,才会计算 (++b &amp;&amp; ++c)。在您的情况下,++a 的计算结果为 true。因此,(++b &amp;&amp; ++c) 永远不会被评估。

【讨论】:

    【解决方案3】:

    逻辑或运算符||(以及逻辑与运算符&amp;&amp;)是少数执行短路运算的运算符之一。

    C standard 的第 6.5.14 节对逻辑 OR 运算符进行了如下说明:

    4 与按位 | 运算符不同,|| 运算符保证 从左到右的评估;如果计算第二个操作数,则有 第一个和第二个评估之间的序列点 操作数。 如果第一个操作数比较不等于 0,则第二个 不计算操作数。

    因为++a 的计算结果为1,所以|| 运算符的结果保证为1,并且不计算右侧。另外,因为&amp;&amp; 的优先级高于||,所以|| 运算符的右侧是++b &amp;&amp; ++c,这意味着++b++c 都不会被计算。

    【讨论】:

      【解决方案4】:

      就优先级而言,x || y &amp;&amp; z 的行为类似于 x + y * z:第二个运算符比第一个运算符绑定得更紧密,这些表达式分别等效于 x || (y &amp;&amp; z)x + (y * z)

      问题中的bc 没有递增的原因是,除了优先级之外,逻辑运算短路了:一旦你走得足够远知道结果,剩下的表达式被跳过。 ||&amp;&amp; 都从左到右评估它们的参数,因此在 a() || b()a() &amp;&amp; b() 中,对 a() 的调用发生在对 b() 的调用之前。

      在简单的情况下,如果a() 返回true,那么在表达式a() || b() 中对b() 的调用将不会被执行,因为它不会影响结果。同样,如果a()返回false,那么在表达式a() &amp;&amp; b()中,对b()的调用将不会被执行。

      在例子中的代码中,bc的增量不会被执行,因为++a产生一个非零值,所以表达式的结果是true而不需要评估++a 之后的任何内容。

      【讨论】:

        【解决方案5】:

        运算符优先级与求值顺序无关。优先级是将不同类型的运算符及其操作数分组的优先级。

        所以,表达式

        ++a || ++b && ++c;
        

        将被评估为

        ++a || (++b && ++c);
        

        逻辑 AND 和逻辑 OR 运算符构成 sequence points,因此保证其操作数的特定求值顺序是从左到右

        Order of evaluation:

        订购
        ……

        • 如果子表达式 E1 和 E2 之间存在序列点, 那么 E1 的值计算和副作用都是 在 E2 的每个值计算和副作用之前排序

        规则
        .....
        2) 在计算以下二元运算符的第一个(左)操作数之后和第二个(右)操作数之前有一个序列点:&&(逻辑与)、|| (逻辑或)和 ,(逗号)。

        逻辑或运算(expr1 || expr2) 采用short-circuiting 行为。也就是说,如果expr1 是逻辑1 (true),则不会评估expr2

        abc的初始值为0。在表达式中:

        ++a || ++b && ++c;
        

        ++a -> 预增a.
        这意味着,表达式++a 的值是a 的递增值,这将是1。由于|| 运算符采用短路行为,因此不会计算|| 的右侧表达式。因此,您将获得输出 - 1 0 0


        为了更好地理解,只需尝试更改表达式中的++a -> a++
        后自增运算符还将操作数的值增加1,但表达式的值是自增操作之前操作数的原始值。因此,a++ 将被评估为0,并且由于短路行为,|| 运算符 (++b &amp;&amp; ++c) 的右侧表达式将被评估。

        逻辑与运算 (expr1 &amp;&amp; expr2) 也采用短路行为。使用逻辑短路,第二个操作数 expr2 仅在结果未完全由第一个操作数 expr1 确定时才被计算。也就是说,只有当expr1 是逻辑1 (true) 并且++b 将导致1 时,才会评估expr2。所以,如果你这样做了

        a++ || ++b && ++c;
        ^^^
        

        输出将是 - 1 1 1

        【讨论】:

          【解决方案6】:

          Precedence 仅控制表达式的解析方式,而不是它们的求值方式。算术* 的优先级高于+,因此a * b + c 被解析为(a * b) + c。但是,abc 中的每一个都可以按任何顺序进行评估。 a * b 的结果必须先知道,然后才能添加到c 的结果中,但这并不意味着a * b 必须在之前 c 进行评估。

          其次,与 C 中的大多数运算符不同,||&amp;&amp; 运算符强制从左到右求值。像a || b &amp;&amp; c 这样的表达式将被解析为a || (b &amp;&amp; c),但a总是首先被计算,并且b &amp;&amp; c 只会在a 的结果为0 时被计算。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多