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
将首先得到评估”。对于像 +
和 *
这样的算术运算符,无法知道是左侧还是右侧先求值。
所以我在阅读不同运算符的顺序,我读到 &&
比 ||
具有更高的重要性,并且它会更快地评估 (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
将首先得到评估”。对于像 +
和 *
这样的算术运算符,无法知道是左侧还是右侧先求值。