为什么“++ x ||” ++ y && ++ z“首先计算”++ x“,即使运算符”&&“的优先级高于”||“

为什么++x || ++y && ++z ++x || ++y && ++z首先计算++x ,即使运算符&&的优先级高于||

R和其他人已经解释了真正发生的事情。 所以,让我只是添加:

你的问题的前提是错误的。 &&具有较高优先级的事实并不意味着围绕它的操作数必须在优先级较低的expression式中的任何操作数之前进行评估。 即使在||的特殊情况下短路 而&&这不一定会如此。

例如,考虑a=b+c+d*e ; *优先级高于+ ,但这并不意味着必须在b+c之前评估d*e 。 这只是意味着在将产品添加到整个expression式之前,必须对其进行评估。 编译器可以将此expression式评估为temp1=d*etemp2=a+ba=temp1+temp2或者可以评估temp1=b+ctemp2=d*ea=temp1+temp2 。 两者同样有效。

随着||的短路行为 和&& ,在评估顺序上还有一些额外的限制。


作为一个方面说明:一般来说,我会避免写这样的代码。 我可以很容易地看到另一个程序员试图读这个代码,只是当增量会发生,什么时候不会发生混淆。 那么,也许如果你使用真正的variables名称,它不会看起来很奇怪。

我偶尔会依靠防止短路的副作用。 喜欢

 if (!eof() && readNextInt())>0 

如果我们已经在档案末尾,我依靠短路来防止阅读,或者

 if (confirmDelete==YES && deleteEntry()!=-1)` 

我依靠第一次testing将虚假短路,所以我不应该删除,当我不应该。 但是这些例子对我来说似乎很简单,我希望任何有能力的程序员都能看到我在做什么。 但是,当这些例子变得模糊的时候,我认为它应该被打破。 考虑

 if (customerType==RETAIL || lastDepositAmount()>100.00) 

如果lastDepositAmount()有副作用,那么如果customerType是零售,这种副作用将永远不会发生。 我不认为这对读者来说一定是显而易见的。 (部分原因是function名称意味着它正在检索数据而不执行任何更新,部分原因是因为客户types和存款金额之间没有明显的关系 – 这听起来像是两个独立的东西)。无可否认,这是主观。 但是,如果有疑问,select简单和清晰,而不是微不足道的性能改进。 总是select简单和清晰的“嘿,这是一个很酷的使用一个晦涩的function的方式,任何人阅读这将是多么聪明,我必须要了解足够的语言来做到这一点印象深刻”

咦?

如果你说&&绑定比||更紧密 ( 这是真的 ),expression式就相当于

 ++x || (++y && ++z) 

由于|| 短路 ,首先需要评估左侧。

如果你的意思是它应该相当于

 (++x || ++y) && ++z 

同样的情况也是如此,因为&&也是短路的,也就是|| 需要首先进行评估,这又使++x成为第一个评估对象。

由于&&||有更高的优先级 ,这意味着你的expression等同于:

 ++x || (++y && ++z) 

因此,在程序甚至开始评估&&的结果之前,必须先评估++x来确定||的右边操作数。 甚至应该被评估(逻辑二元运算符是“短路”,这意味着如果左侧足以确定结果,它们不评估右侧)。 这里没有什么可疑或编译器特定的。 这个expression式的行为完全由C标准规定,在任何编译器上都是一样的。 如果xyz都是相同的variables,它甚至会起作用,因为||&&引入序列点。

这里有可怕的错误答案。

该expression式的评估顺序不依赖于实现 。 它是明确的!

由于&&绑定高于|| ,这个expression式相当于++x || (++y && ++z) ++x || (++y && ++z) 。 此外, &&||短路的 ,所以如果expression式的左边足以确定该值,则不评估右边。

这是什么时候? 那么,我们知道,不论a的值是什么, False && a总是归结为False ,特别是即使a不是一个单一的值而是一个复杂的expression式。 所以我们根本不评价。 同样, True || a True || a总是解决为True

这导致在这个例子中非常确定的评估顺序: 第一个 ++x然后 (如果有的话) ++y 然后 (再次:如果有的话) ++z

如图所示,运营商使用波兰树数据结构进行评估,这是深度优先评估algorithm。 让我们给这些操作命名:A = ++ x B = ++ y C = ++ z D = B && C E = C || D评估顺序:A,B,C,D,E。当然,C有一个优化,如果A是真的,那么E将被评估为真,而不评估BC和D.同样,如果B是假的,不能评估以优化评估速度。

替代文字

++具有最高优先级,作为其前置增量。 这意味着这个操作的结果值被传递给二元运算符。 然而,有趣的是++ x的短路或与其他的短路。 如果++ x是真的,那么其余的可能根本就没有完成。

评估的先后次序和顺序并不是一回事 ,特别是在这种情况下。 所有的优先级都告诉你这个expression式应该被parsing为

 ++x || (++y && ++z) 

IOW,这意味着OR的expression式是++x(++y && ++z) 。 这并不意味着(++y && ++z)将在++x之前被评估

对于逻辑运算符||&& ,评估总是从左到右( 在线C标准 ,6.5.13和6.5.14节)。 对于任何expression

 a || b 

a将被完全评估; b除非a的结果是0,否则不会被评估(认为++xb代表++y && ++z )。 同样,对于任何expression

 a && b 

a将被完全评估; 除非a的结果不是0,否则b不会被评估。

所以对于你的情况,expression式++x首先被评估。 如果结果是0, 那么只有这样才能评估++y && ++z

||&&是特殊的,评估的顺序是保证从左到右。 位运算符或算术运算符不是这样。 例如,在expression式中

 ++x + ++y * ++z 

expression式++x++y++z可以按任意顺序评估; 所有的优先级都告诉你, (y+1) * (z+1)的结果将被加到x+1的结果中。

前两个答案解释得相当好…我会指出|| 是“OrElse”。

如果左侧是真的,右侧不会被触及。 如果右侧是错误的,&&(AndAlso)的左侧不会被触及。

如果您希望双方都增加,请使用| 和&。

http://msdn.microsoft.com/en-us/library/2h9cz2eb(v=VS.80).aspx

如果编译的代码根据另一个expression式的结果可以绕过一个expression式的评估,则逻辑操作被认为是短路的。 如果评估的第一个expression式的结果确定了操作的最终结果,则不需要评估第二个expression式,因为它不能更改最终结果。 短路可以提高性能,如果绕过的expression是复杂的,或者如果它涉及程序调用。

MSDN有表格显示如何部分“(未评估)”。

为什么碰触第二部分,如果第一部分确定性地预先确定结果?

我和C一起工作已经很久了,但是如果我没有记错的话,++是一个一元运算符,它往往比二元运算符先例。 如果你仔细想想,这是有道理的。

如果您的操作对顺序敏感,则不应该依赖于操作员的优先级,因为不同的编译器(以及同一版本的不同版本)可以以不同的方式实现优先级。

无论如何b定义++ x意味着x必须在使用之前递增,所以你会期望它在执行时被赋予高优先级。

不不不

 if (++x || ++y && ++z) { /* ... */ } 

 ++x; ++y; ++z; if (x || (y && z)) { /* ... */ } 

编辑对等投诉后:)

是!

 if (is_data_valid(x, y, z)) process_data(&x, &y, &z);