C 中的短路和运算符优先级

Short circuit and operator precedence in C

我知道 C 中的逻辑运算符遵循短路,但我怀疑短路和运算符优先级规则是否相互对立。请参阅以下示例:

#include<stdio.h>
int main()
{
    int a;
    int b=5;

    a=0 && --b;
    printf("%d %d",a,b);

    return 0;
}

根据优先级规则,前缀运算符的优先级最高。所以应该先计算 --b,然后计算 &&,最后将结果分配给 a。所以预期的输出应该是 0 4。但在这种情况下,&& 的第二个操作数从未实际执行,结果是 0 5.

为什么这里没有应用优先规则。逻辑运算符是否不受优先规则约束?如果是,还有哪些其他运营商表现出这种行为?这种行为背后的逻辑是什么?

operator && 的求值顺序是从左到右。

= 的优先级较低,实际上只有 ooperator , 的优先级低于 =.

所以表达式将读作 a = (0 && --b) 是 0 首先根据提到的评估顺序评估。

由于 0 的计算结果为 false,因此无需计算表达式的第二部分,因为 false && truefalse,给定表达式的第一部分表达式为 false,表达式将始终为 false.

如果您有 || 运算符,则必须计算表达式的第二部分。

优先级 影响歧义表达式的解析方式。当有多种方法可以用多个运算符解释一个表达式时,优先级会告诉我们哪种解释是正确的。将优先级视为一种找出隐含括号位置的机制。

例如,在所讨论的语句中,有两种有效的解析方法。如果 = 的优先级高于 && 它可以读作:

(a = 0) && --b;

但由于&&具有更高的优先级,它实际上被解释为:

a = (0 && --b);

(注意:您的代码格式表明它是第一个。注意不要误导!)

评估顺序与优先级不同。它们是相关但独立的概念。在使用优先级确定表达式的正确解析之后,评估顺序告诉我们评估操作数的顺序。它是从左到右吗?右到左?同时?未指定?

大部分评估顺序未指定。 +*<< 等运算符没有定义的评估顺序。编译器可以为所欲为,程序员不得编写依赖于任何特定顺序的代码。 a + b 可以评估 a 然后 b,或者 b 然后 a,或者它甚至可以交织他们的评估。

=&& 等例外。 = 总是从右到左计算,&& 从左到右短路。

以下是对我们的声明进行逐步评估的方式:

  1. a = (0 && --b)= 从右到左计算
    1. 0 && --b, && 评估从左到右短路
      1. 0, 判断为 false 触发短路并取消下一步
      2. --b,短路未评估
      3. 结果是 0
    2. a,已计算变量引用
    3. a = 0,赋值发生,整体结果为0

You said that there is no specific order for + and *, but this table shows the order to be left to right. Why so?

table 的最后一列是关联性。当我们使用相同的运算符两次,或者当我们使用具有相同优先级的运算符时,结合性打破了优先级关系。

比如我们应该怎么读a / b / c。是吗:

  • (a / b) / c,或
  • a / (b / c)?

根据table /具有从左到右的结合性,所以是第一个

foo = bar = baz 这样的链式赋值呢?现在,赋值具有从右到左的关联性,因此正确的解析是 foo = (bar = baz).

如果这一切变得混乱,请关注一个简​​单的经验法则:

"Precedence and associativity are independent from order of evaluation."

运算符优先级并不一定表示表达式先执行,它只是表示表达式被解析为高优先级运算的结果用于低优先级运算,而不是相反大约。实际的表达式只有在需要时才会被评估!

您将两个相关但不同的主题混为一谈:运算符优先级求值顺序

运算符优先级规则规定了各种运算符如何组合在一起。在这个表达式的情况下:

 a=0 && --b;

运算符分组如下:

 a = (0 && (--b));

但这对操作数的计算顺序没有影响。&& 运算符特别规定首先计算左操作数,如果它的计算结果为 0,则不计算右操作数。

所以在这种情况下 && 的左侧 0 被评估,因为它是 0 右侧 --b 不被评估,所以 b 不递增。

这是运算符优先级和求值顺序之间差异的另一个示例。

int val()
{
    static x = 2;
    x *= 2;
    return x;
}

int main()
{
    int result = val() + (5 * val());
    printf("%d\n", result);
    return 0;
}

上面的程序会打印什么?事实证明,有两种可能性,而且都是有效的。

在这个表达式中:

val() + (5 * val())

没有运算符具有任何类型的短路行为。因此编译器可以自由地以任何顺序评估 +* 的各个操作数。

如果val() 的第一个实例首先被评估,结果将为4 + ( 5 * 8) == 44。如果先计算 val() 的第二个实例,则结果将为 8 + (5 * 4) == 28。同样,两者都是有效的,因为可以按任何顺序计算操作数。

运算符优先级并不是游戏中的全部。还有评估顺序。并且要求首先评估 a=0(评估顺序是从左到右),然后根本不评估 && 之后的右侧部分。

这就是 C 的工作原理。