如果条件,评估OR'd

时间:2013-12-18 12:21:02

标签: c if-statement conditional-statements evaluation

我有一个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();

5 个答案:

答案 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情况下,会发生以下情况:

  • x和y都是从uint8_tint的整数提升,因为它们是小整数类型。
  • 表达式(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==truey==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运算,并且只有变量的最终值才会在条件中使用。

结果是一样的,但是&和|,你确定你的病情的所有表达都将被评估。