2011-10-10 205 views
3

我碰到这样的代码:&&和||运营商

int main() 
    { 
     int i=1,j=2,k=0,m=0; 
     m = ++i || ++j && ++k; 
     printf("%d %d %d %d %d",i,j,k,m); 
    } 

程序返回2 2 0 1...。为什么?

&&||具有更高的优先级,因此++j && ++k应该先评估。因此我期望j=3k=1。它将返回true,因此||成为真,因此++i不应被评估。但它可以用其他方式工作。

我想别人向我解释。

+20

哦,不,不会再 –

+0

这是一个常见的问题功课这些天? – crashmstr

回答

11

具有较高的优先级并不意味着它就会先计算。这只是意味着它更紧密。在该示例中,该表达式等同于:++i || (++j && ++k)。首先评估的是++i,因为||从左到右评估。只有在评估结果为false时才会评估++j && ++k,因为||是短路。

+3

's/priority/precedence /' –

9

其实++i将首先评估。只有当它是假的时候才会对右侧进行评估(在你的情况下不是)。

“& &具有更高优先级”这一事实涉及优先级(操作数紧贴它)而不是“其操作数首先得到评估”。

由于&&确实上表||,表达将被解释如下:

m = ++i || (++j && ++k) 
0

因为||和& & short circuit,因此指定sequence points

注:这是最初标记C++以及,你会得到一个稍微不同的答案有作为重载运算符||或& &不要短路只是内置那些

2

基本上||手段,“如果你收到了一些东西,是真的,返回,否则,返回无论事后发生的情况。”所以,唯一评估的是m = (++i != 0)。这意味着“增加i,将m分配给与0相比的值,打破”。

更具体地讲,这是发生了什么事:

i = 1; 
i = i + 1; 
if(i) { 
    m = 1; 
} 
else { // who cares, this will never happen. 
    j = j + 1; 
    if(j) { 
     k = k + 1; 
     m = (k != 0); // 1 
    } 
    else { 
     m = 0; 
    } 
} 
4

Short circuit evaluation。如果&&的左侧不为零,则只有在那时才会评估右侧。同样,只有当||的左侧为零时,才会评估右侧。

0

快捷键运算符将导致不必要的表达式组件不被评估。由于& &具有更高的优先级,因此如果您希望允许||,则需要最后对其进行评估。运算符能够在++计算结果为true时快捷整个表达式。既然是这样,++ i是在“=”之后评估的唯一变量。

4

“较高的运算符优先级”与“首先评估的”不一样。当您使用短路操作时,它们从左到右进行评估。任何算术运算的结果都会受到运算符优先级的影响,但这并不会改变短路的左t0右排序。

你的例子的复杂性是不这样做的一个很好的理由。即使你知道规则并确切知道它会做什么,下一位编程人员来看看代码可能不会。

0

您在这里看到逻辑运算符短路。如果||条件的第一部分为真,那么它永远不会评估表达式的其余部分(因为如果第一部分是指针非空检查,那么如果第二部分中的指针为空,则不想取消引用第二部分中的指针)。此外,由于它在布尔结果表达式中,所以++i的结果在被分配到m之前被转换回bool1

避免像瘟疫这样的代码,它只会给你短期和长期调试噩梦。

0

评价的先后顺序和顺序是两回事。 ||&&都从左到右评估它们的操作数;优先权不会改变这一点。

鉴于表达式a || b && c,将首先评估a。如果结果为0,则将评估b && c

0

比较运算符的顺序(||和& &)更重要。这就是为什么你最好先放置你最重要的测试。

1

在C语言中,有你需要知道的两个不同的问题:运算符优先级为了评估的。

运算符优先级决定哪个运算符获得首先评估的操作数,以及属于哪个运算符的哪些操作数。例如在表达式a + b * c中,运算符*的运算符优先级高于+。因此,表达式将被评估为

a + (b * c)

在C语言中的所有运营商有确定性优先级,他们是在任何编译器是相同的。

评估顺序决定哪个操作数首先得到评估。请注意,子表达式也是一个操作数。评估顺序在运营商优先级确定后应用。如果上面的a + b * c示例具有从左到右的评估顺序,则操作数本身将按照a,b,c的顺序进行评估。如果操作数是例如函数调用,那么函数a(),b()和c()将按照该顺序执行。这个例子中的所有操作数都需要被评估,因为它们都被使用了。如果编译器可以确定某些操作数不需要进行评估,则可以优化它们,而不管这些操作数是否包含副作用(如函数调用)。

的问题是操作数的计算顺序是最常见的不确定的行为,这意味着编译器是免费的评估是左到右或从右到左,我们无法知道或承担任何事它。对于C中的大多数操作数来说,这是事实,除了评估顺序始终是确定性的一些例外。这些是运营商|| && ?: ,的操作数,其中评估的顺序保证为从左到右。 (当使用正规的C语言的语义,一个说有左的评价和右操作间的时序点。)


所以对于具体的例子m = ++i || ++j && ++k

  • 一元前缀++运算符的优先级最高,它们将运算符i,j和k绑定到它们。这个语法非常直观。
  • 二进制& &运算符具有第二高的优先级,将运算符++j++k绑定到它。所以表达式相当于m = ++i || (++j && ++k)
  • 二进制||运营商具有第三高优先级,将运营商i++(j++ && ++k)=绑定到它。
  • 赋值操作符=具有最低的优先级,结合运营商m++i || (++j && ++k)到它。

此外,我们可以看到,无论是||并且运营商是保证评估顺序保持从左到右的那些运营商之一。换句话说,如果||的左操作数运算符被评估为true,编译器不需要评估正确的操作数。在具体的例子,++i总是正的,所以编译器可以执行相当的优化,有效地改造了表达m = ++i;

如果i值在编译时不知道,编译器会被强行评估整个表达。然后表达式将按此顺序进行了评价:

  • & &比||优先级高,所以开始评估& &操作。
  • & &操作者保证具有左到右操作数的评价顺序,因此首先执行++Ĵ。
  • 如果++ j的结果为真(大于零),并且只有这样,则评估正确的运算符:perform ++ k。
  • 商店++Ĵ& & ++ K的临时变量的结果。我在这里将其称为j_and_k。如果++ j和++ k都为正,则j_and_k将包含值1(真),否则包含0(假)。
  • ||运算符保证有操作数从左到右的评估顺序,所以先执行++ i。
  • 如果++ i是假(零),也只有这样,评估右操作“j_and_k”。如果其中一个或两个都是肯定的,则||的结果运算符为1(真),否则为0(假)。
  • m取决于结果获取值1或0。