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

时间:2010-09-13 12:28:44

标签: c operator-precedence

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

11 个答案:

答案 0 :(得分:42)

咦?

如果您说&&||which is true)更紧密,则表达式等同于

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

|| short-circuits起,它需要先评估左侧。

如果你的意思是它应该等同于

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

同样的情况仍然如此,因为&&也会短路,这意味着需要首先评估||,这反过来使++x成为第一件要评估的东西。

答案 1 :(得分:33)

由于&&的优先级高于||,因此表示您的表达式等同于:

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

因此,在程序开始评估&&的结果之前,它必须评估++x以确定是否应该评估||的右手操作数(逻辑二进制)操作员“短路”,这意味着如果左侧足以确定结果,他们不评估右侧。这里没有什么可疑的或编译器特定的。此表达式的行为完全由C标准指定,并且在任何编译器上都是相同的。如果xyz都是相同的变量,它甚至会起作用,因为||&&会引入序列点。

答案 2 :(得分:24)

UnwindR和其他人解释了实际发生的事情。所以,我只想补充一下:

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

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

由于||&&的短路行为,对评估顺序有一些额外的限制。


作为旁注:一般来说,我会避免编写这样的代码。我可以很容易地看到另一个程序员试图阅读这段代码,对于增量何时发生以及什么时候不发生而感到困惑。好吧,也许你使用真正的变量名称,它看起来不会那么奇怪。

我偶尔会依靠短路来防止副作用。像

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

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

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

我依靠第一个测试来对错误进行短路,所以当我不应该这样做时我不会删除。但这些例子对我来说似乎很简单,我希望任何有能力的程序员都能看到我在做什么。但是当这些例子变得神秘时,我认为它需要被打破。考虑

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

如果lastDepositAmount()有副作用,那么如果customerType是零售,那么这种副作用将永远不会发生。我不认为这对读者来说一定是显而易见的。 (部分原因是函数名称暗示它正在检索数据而不执行任何更新,部分原因是客户类型与存款金额之间没有明显的关系 - 这听起来像两个独立的事情。)不可否认,这是主观。但是如果有疑问,请选择简单明了而不是简单的性能提升。总是选择简单和清晰,而不是“嘿,这是对一个不起眼的功能的一种很酷的使用方式,任何读这篇文章的人都会对我必须如何聪明地理解语言能够做到这一点留下深刻印象”。< / p>

答案 3 :(得分:16)

这里有非常错误的答案。

此表达式的评估顺序不依赖于实现。它定义明确!

由于&&绑定高于||,因此此表达式等同于++x || (++y && ++z)。此外,&&||都是短路的,所以如果表达式的左侧足以确定值,则右侧从不评估。

这是什么情况?好吧,我们知道False && a始终会解析为False,无论a的值是什么,特别是即使a不是单个值,而是复杂表达式。因此,我们根本不评估a。同样,True || a始终会解析为True

这导致在此示例中有一个非常明确的评估顺序: first ++x then (如果有的话)++y和< em>然后(再次:如果有的话)++z

答案 4 :(得分:6)

使用波兰树数据结构评估运算符,如图所示,它是深度优先评估算法。 让我们为操作命名: A = ++ x B = ++ y C = ++ z D = B&amp;&amp; C E = C || d 评估顺序:A,B,C,D,E。 当然,C有一个优化,如果A为真,那么在不评估B C和D的情况下,E将被评估为真。同样,如果B为假,则不会评估C以优化评估速度。

alt text

答案 5 :(得分:2)

++具有最高优先级,因为它的 pre 增量。这意味着此操作的结果值将传递给二元运算符。然而有趣的是++ x的短路或其他的短路。如果++ x为真,则其余部分可能根本不会完成。

答案 6 :(得分:2)

评估的优先顺序和顺序不是一回事,尤其是在这种情况下。所有优先级都告诉您表达式应该被解析为

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

IOW,这意味着OR一起表达的是++x(++y && ++z)。它 意味着(++y && ++z)将在++x之前评估

对于逻辑运算符||&&,评估始终从左到右(Online C Standard,第6.5.13和6.5.14节) 。对于任何表达

a || b

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

a

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

因此,对于您的情况,首先评估表达式++y && ++z。如果结果为0,则||进行评估。

&&++x + ++y * ++z 的特殊之处在于评估顺序保证是从左到右。对于按位运算符或算术运算符,情况并非如此。例如,在表达式

++x

表达式++y++z(y+1) * (z+1)可以按任何顺序评估;所有优先级都告诉您,x+1的结果将添加到{{1}}的结果中。

答案 7 :(得分:1)

前两个答案很好地解释了......我会指出||是“OrElse”。

如果左侧是真的,右侧不会被触及。左侧&amp;&amp;如果右侧是假的,那么(AndAlso)也不会被触及。

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

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

  

据说是逻辑运算   如果编译代码短路   可以绕过一个人的评价   表达取决于结果   另一个表达。如果结果   评估的第一个表达式   确定最终结果   操作,没有必要   评估第二个表达式,   因为它无法改变决赛   结果。短路可以改善   表现如果绕过表达式   是复杂的,还是涉及到的   程序调用。

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

为什么触摸第2部分,如果第1部分确定性地预先确定结果?

答案 8 :(得分:0)

自从我使用C以来已经很长时间了,但是如果我没记错的话,++是一元运算符,它总是先于二元运算符。如果你仔细想想就行了。

答案 9 :(得分:-1)

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

无论如何b定义++ x意味着x必须在使用之前递增,因此你可以期望它在执行中具有高优先级。

答案 10 :(得分:-3)

不不不

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

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

同行投诉后

编辑:)

YES!

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