我有一个if
语句,其中包含一个语句,但条件由多个OR表达式组成。
在执行期间,如果OR'd条件中的第一个表达式为真,那么第二个表达式是否仍会被评估?
即。是否更好地使用OR'd表达式,或者让每个表达式形成else
的单个条件,其中每个else
的主体是相同的?
e.g
if ((!x && y && z) || (x && !y && !z) || (x != y) {
foo();
//vs
if (!x && y && z)
foo();
else if (x && !y && !z)
foo();
else if (x != y)
foo();
答案 0 :(得分:5)
不,如果OR(AND)的左侧评估为true(false),则不评估右侧。它被称为短路,是C的一个众所周知的特性。
您可以通过
进行检查int f () {
printf("f!\n");
return 1;
}
int g () {
printf("g!\n");
return 0;
}
int main () {
if(f() || g())
printf("hey\n");
}
这会打印
f!
hey
答案 1 :(得分:2)
第一个代码是“更好”,因为在使用||
时,返回true
的第一个评估将结束对条件的评估。如果您仍想评估所有条款,请使用单个|
答案 2 :(得分:1)
简答:否
C使用||
和&&
逻辑运算符短路所有if语句。没有办法解决这个问题。事实上:C has no eager operators。
这背后的原因是,如果您想要在1,2,3,......中的任何一个情况下执行单个操作,那么如果只有其中一个案例,则无需评估所有案例是真实的。换句话说,短路评估更快 因此,只要它不妨碍可读性,您的第一个代码段就是更好的选择。
考虑以下(伪代码):
if (is_alphabet_letter('a') || is_alphabet_letter('b') || is_alphabet_letter('c'))
puts("Found letter of the roman alphabet");
这将导致只有1次调用is_alphabet_letter
函数,而不是3次。就结果程序而言,上述代码段评估为:
if (true /*|| jibberish, don't care*/)
puts("...");
鉴于此:
if (is_alphabet_letter('a'))
puts("");
else if (is_alphabet_letter('b')
puts("");
else if (is_alphabet_letter('c')
puts("");
只是添加杂乱,并且将在所有可能性中由编译器优化,使其更像第一个语句。
这同样适用于&&
,顺便说一下:
if (false && true && a_complex_function())
永远不会导致对a_complex_function
的调用,这很简单,因为第一个表达式的计算结果为false
,因此检查任何剩余的表达式是没有意义的。 C只是移动到else
块或下一个语句
这就是为什么选择if
块中表达式的顺序有时会产生影响的原因:
if (a_complex_function() && false)
将始终调用该函数。有时,这可能是你想要的,但有时它不是,所以考虑将更复杂的表达式移到if语句的末尾并没有什么坏处。
答案 3 :(得分:1)
这两种情况最有可能产生相同的机器代码,或非常接近的情况。所以选择哪一个是编码风格的问题。
我会说,如果操作很复杂,以至于它们不能轻易放在一行上,那么if
- else if
就更喜欢了。否则,如果操作有点简单,||
版本可能更喜欢。但这是相当主观的。
关于这些运营商的内部运作的高级讨论如下。如果你对这些事情不是很感兴趣,你可以在这里停止阅读!
然而,有一些细节使这两个版本略有不同。在这两种情况下,使用类型平衡隐式提升每个单独的操作数。但是在||
的情况下,会发生额外的隐式促销:
(!x && y && z)
的结果与(x && !y && !z)
的结果相平衡。最后,结果是具有最大转换等级的操作数的类型。
重要吗?在这个特定的情况下,我看不出它会怎么样。但是我们可以说我们有类似的东西:
uint8_t x;
uint8_t y;
uint32_t z;
然后我们有一个表达式:
if( (x && y) || (x && z) )
// or
if(x && y)
{}
else if(x && z)
{}
假设我们使用的是8位或16位CPU,int
为16位。
在if-else情况下,会发生以下情况:
uint8_t
到int
的整数提升,因为它们是小整数类型。(int)x && (int)y
是平衡的。 x && y
计算的类型为int
,结果类型为int
。x && z
中,x
按上述方式进行整数提升,但不是z
,它不是小整数类型,仍为uint32_t
。(int)x && (uint32_t)z
是平衡的,然后根据类型uint32_t
计算。结果是uint32_t
类型。x && y
总是在16位变量上计算,而x && z
总是在32位变量上计算。如果我们查看||
版本,会发生同样的事情。但是,我们还有另一个额外的平衡:(uint16_t)first_result || (uint32_t)second_result
。由于||
运算符,强制执行此平衡。这意味着,如果x==true
和y==true
,则整个操作的结果将为uint32_t
类型。
但在if-else版本中,x && y
类型始终为uint16_t
。 ||
版本在x
类型和z
类型之间引入了模糊的紧密耦合。
编译器是否能够有效地优化此||
方案,我不知道。我希望如此,但由于编译器不能对整个表达式的结果进行编译时决定,因为它无法知道操作数是真还是假,它可能最终会执行所有操作在32位类型上。如果我们有一个8位或16位CPU,这将是一个坏消息,它将非常低效地处理32位数。
答案 4 :(得分:-3)
不,双逻辑运算符(&&和||)将逐个评估每个参数,直到找到一个令人满意的结果。
在&&的情况下,将评估所有参数,直到找到false为止,在这种情况下,表达式条件为false,并且不会计算if代码。
在||的情况下,将对参数进行求值,直到找到true为止,在这种情况下,表达式条件为true,并且将对if代码进行求值。
如果您想要评估表达式的所有元素,请使用&和|运营商。他们将创建一个变量,该变量将由表达式的所有元素进行AND运算或OR运算,并且只有变量的最终值才会在条件中使用。
结果是一样的,但是&和|,你确定你的病情的所有表达都将被评估。