我知道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
。
为什么这里没有应用优先级规则。逻辑运算符是否不受优先级规则的约束?如果是,还有哪些运营商表现出这种行为?这种行为背后的逻辑是什么?
您合并了两个相关但不同的主题:运算符优先级和求值顺序。
运算符优先级规则规定了如何将各种运算符组合在一起。在这个表达式的情况下:
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("%dn", result);
return 0;
}
上面的程序将打印什么?事实证明,有两种可能性,而且都是有效的。
在这个表达式中:
val() + (5 * val())
没有任何具有任何类型短路行为的操作员。因此编译器可以自由地按任何顺序计算+
和*
的单个操作数。
如果首先评估val()
的第一个实例,则结果将为4 + ( 5 * 8) == 44
。如果首先评估val()
的第二个实例,则结果将为8 + (5 * 4) == 28
。同样,两者都是有效的,因为操作数可以按任何顺序进行求值。
优先级影响如何解析不明确的表达式。当有多种方法可以用几个运算符解释一个表达式时,优先级告诉我们哪种解释是正确的。将优先级视为一种机制,以确定隐含的括号在哪里。
例如,在所讨论的语句中,有两种有效的解析方法。如果=
的优先级高于&&
,则可以读取为:
(a = 0) && --b;
但由于&&
具有更高的优先级,它实际上被解释为:
a = (0 && --b);
(注意:你的代码格式表明它是第一个。小心不要误导!)
评估顺序与优先级不同。它们是相关但独立的概念。在使用优先级确定表达式的正确解析后,求值顺序告诉我们中操作数的求值顺序。是从左到右吗?从右向左?同时的未指定?
在大多数情况下,未指定评估顺序。像+
、*
和<<
这样的运算符没有定义的求值顺序。编译器可以做任何它喜欢做的事情,程序员不能写依赖于任何特定顺序的代码。a + b
可以评估a
然后b
,或者b
然后a
,甚至可以将它们的评估交织在一起。
=
和&&
等是例外。=
总是从右到左进行评估,而&&
是从左到右进行短路评估。
以下是我们声明的评估过程:
a = (0 && --b)
、=
从右到左评估0 && --b
、&&
通过短路从左到右进行评估0
,评估为false,触发短路并取消下一步--b
,由于短路而未进行评估- 结果为
0
a
,评估的变量参考a = 0
,分配发生,总结果为0
您说过
+
和*
没有特定的顺序,但此表显示了从左到右的顺序。为什么会这样?
该表的最后一列是关联性。当我们两次使用同一运算符时,或者当我们使用具有相同优先级的运算符时,关联性会破坏优先级关系。
例如,我们应该如何阅读a / b / c
。是吗
(a / b) / c
,或a / (b / c)
根据表/
具有从左到右的关联性,因此它是第一个。
像foo = bar = baz
这样的链式作业呢?现在,赋值具有从右到左的关联性,因此正确的解析是foo = (bar = baz)
。
如果这一切变得令人困惑,请关注一个简单的经验法则:
"优先级和关联性独立于评估顺序。">
运算符优先级并不一定表示表达式首先执行,它只是意味着对表达式进行解析,以便在较低优先级的运算中使用较高优先级运算的结果,而不是相反。实际表达式只有在需要时才进行求值!
operator &&
的求值顺序是从左到右。
=
的优先级较低,实际上只有ooperator ,
的优先级低于=
。
因此,在给定上述评估顺序的情况下,表达式将读取首先评估的a = (0 && --b)
为0。
由于0
的求值为false
,因此不需要求值表达式的第二部分,因为false && true
是false
,给定表达式的第一部分是false
,则表达式将始终是false
。
如果使用||
运算符,则必须对表达式的第二部分进行求值。
运算符优先级并不是游戏中的全部内容。还有评估的顺序。这要求首先评估a=0(评估顺序是从左到右),然后在&;根本不进行评估。
这就是C的工作原理。