关于运算符优先级的示例

An example about operator precedences

据我所知,一元运算符优先于 ||&&。在下面的代码中,我期望所有输出都等于 1 的输出。是的,存在短路,但不应该在 ||&& 之前计算那些预增量吗?这些优先级在这里如何工作?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a = 0, b = 0, c = 0;
    a = b = c == 1;
    c = ++a || ++b && ++c;    // short-circuit here
    printf("a=%d, b=%d, c=%d\n", a, b, c);
}

Output:
a=1 b=0 c=1

我认为您感到困惑 operator precedence with order of evaluation。这是发生的事情的概要。在评论中,我对 = 的使用是身份的使用——很像数学上的等号。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // all variables are assigned the value 0
    int a = 0, b = 0, c = 0;

    // a and b are equal to the evaluation of `c == 1`, which is false. a and b are 0
    a = b = c == 1;

    /* 
       && has higher precedence than ||, so ++b and ++c are "grouped": ++a || (++b && ++c)
       ++a is evaluated, a = 0+1 = 1. 1 is true, short circuit the second grouping. 
       c = a = 1.
    */
    c = ++a || ++b && ++c;    // short-circuit here

    // a = c = 1, b = 0
    printf("a=%d, b=%d, c=%d\n", a, b, c);
}

运算符优先级求值顺序没有任何关系。更高的优先级意味着首先完成对该运算符的操作数分组。

在声明中

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

先完成++操作数的grouping/binding,然后分别完成&&||的操作数。为了表明这一点,我在表达式

中添加了括号
  • ++ 具有更高的优先级,因此首先将操作数绑定到它

    c = (++a) || (++b) && (++c); 
    
  • && 的优先级高于 ||

    c = (++a) || ((++b) && (++c)); 
    
  • || 的优先级高于 =

    c = ((++a) || ((++b) && (++c)));   
    
  • = 的优先级最低

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

|| 短路和最低优先级运算符这一事实解释了结果。因为 ++ 的优先级高于 &&&& 的优先级高于 ||,所以 ++a || ++b && ++c 的表达式树是:

       ||   -- left:   ++a
            -- right:  &&   --left:  ++b
                            --right: ++c

因此,为了计算表达式,C 首先考虑计算 || 的规则,由 C11 标准中的 6.5.14 给出。具体来说:

6.5.14.4: Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated.

||短路的事实意味着它首先评估其左操作数,并且仅在左操作数为零时才评估其右操作数。因此,要计算表达式 ++a || ++b && ++c,C 需要以下内容:

  1. 计算 ++aa 递增 1,表达式等于其增量值)。如果它非零,则 || 表达式等于 1 并且永远不会计算右侧。
  2. 否则,按从左到右的顺序计算 ++b++c。如果 ++b 为零,则永远不会计算 ++c。如果两者都非零,则表达式等于 1.

因为 ++a 的计算结果为 1,所以永远不会计算 || 的右侧。这就是为什么你有 a==1b==0c==1.

语句 c = ++a || ++b && ++c 还有一个问题,即该语句有可能调用未定义的行为。如果 ++a 为假且 ++b 为真,则必须评估 ++c。但是,c = ...++c之间没有序列点。由于表达式都修改 c 且中间没有序列点,因此行为未定义。有关此内容的进一步说明,请参阅