C中检查条件的顺序

Order of checking the conditions in C

所以我在阅读不同运算符的顺序,我读到 &&|| 具有更高的重要性,并且它会更快地评估 (source)。然后有人问这段代码会打印什么:

#include <stdio.h>
int main(){
    int a=0, b=0, c=0, d=0;
    if(a++>0 || ++b==1 || c--<=0 && d++>c--){
        printf("if\na:%d\nb:%d\nc:%d\nd:%d\n",a,b,c,d);
    }
    else{
        printf("else\na:%d\nb:%d\nc:%d\nd:%d\n",a,b,c,d);
    }
    return 0;
}

而且我认为 c-- <= 0 && d++ > c-- 会先求值,总的来说是对的。处理后,c 等于 -2,d 等于 1。然后它会从左侧开始检查,评估 a++ > 0 || ++b == 1 为真,a 将在末尾为 1,并且 b 在条件中和之后为 1。所以总条件是 true || true 并且它是真的,所以我们将打印:

if
a:1
b:1
c:-2
d:1

是吗?显然,不。我已经在我的系统 (Windows 10) 和在线编译器 (this one) 上使用 GCC (Mingw) 对其进行了测试,并且都打印了:

if
a:1
b:1
c:0
d:0

我已将条件更改为:if(a++>0 || ++b==1 || (c--<=0 && d++>c--) ) 但两个地方的输出完全相同。有什么我不注意的吗?或者这是一个错误?几乎看起来 ||&& 具有相同的优先级,整个事情是从左侧评估的,并且发生短路和其他事情。如果我将 ++b==1 部分更改为 ++b==0,则输出与我预测的相同。
在此先感谢您提供的任何帮助 :)

本题表达方式:

if(a++>0 || ++b==1 || c--<=0 && d++>c--)

是一个可怕的、可怕的表达的典型例子,非常不切实际和不切实际,而且非常难以理解,但它很好地说明了一个非常重要的观点:precedence is not the same作为评估顺序.

优先级真正告诉我们的是运算符是如何与它们的操作数联系起来的。所以给定简化表达式

A || B || C && D

第一个 ||、第二个 ||&& 实际结合在一起并对其进行运算的是哪两个子表达式?如果您是一名编译器编写者,您可以通过构建一个“解析树”来回答这些问题,该树明确显示了哪些子表达式与哪些运算符一起使用。

那么,给定表达式 A || B || C && D,表达式的解析树是否如下所示:

        &&
       /  \
     ||    D
    /  \
  ||    C
 /  \
A    B

或者像这样:

  ||
 /  \
A    ||
    /  \
   B    &&
       /  \
      C    D

或者像这样:

      ||
     /  \
    /    \
  ||      &&
 /  \    /  \
A    B  C    D

要回答这个问题,我们不仅需要知道&&的优先级高于||,而且||是左结合的。鉴于这些事实,表达式

A || B || C && D

被解析为好像已经写好了

(A || B) || (C && D)

因此,结果是我展示的三个候选解析树中的第三个:

      ||
     /  \
    /    \
  ||      &&
 /  \    /  \
A    B  C    D

但现在我们可以真正了解 ||&& 运算符的“短路”行为将如何应用。 “top”|| 将评估其左侧,然后,如果它为假,则还评估右侧。同样,下方的 || 将计算 左侧。因此,无论如何,A 将首先得到评估。对于原始问题中的表达式,对应于 a++ > 0.

现在,a++>0 为假,因此我们将不得不计算 B,即 ++b == 1。现在, 真,所以第一个 || 的结果是“真”。

所以第二个(顶部)|| 运算符的结果也是“真”。

所以顶部 || 运算符的右侧根本不需要计算。

所以包含 && 的整个子表达式根本不会被计算。

所以即使 && 具有最高的优先级,它最终被考虑到最后,并且(因为左边的东西涉及 || 并且是真的)它最终没有得到评估完全没有。

正如我一开始所说的,底线是优先级并不能决定求值顺序。

此外,如果其他地方没有说明,这种有保证的从左到右的行为 保证 ||&&运算符(并且以不同的方式用于三元 ?: 运算符)。如果表达式是

A + B + C * D

不是,正如我之前所说,“无论如何,A 将首先得到评估”。对于像 +* 这样的算术运算符,无法知道是左侧还是右侧先求值。