Why does “++x || ++y && ++z” calculate “++x” first

2019-01-01 09:19发布

Why does ++x || ++y && ++z calculate ++x first, even though the precedence of operator && is higher than ||?

11条回答
旧人旧事旧时光
2楼-- · 2019-01-01 09:52

No no no

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

Yes

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

Edit after peer complaints :)

YES!

if (is_data_valid(x, y, z)) process_data(&x, &y, &z);
查看更多
残风、尘缘若梦
3楼-- · 2019-01-01 10:01

Top two answers explain it rather well... I'd point out that || is "OrElse".

Right side doesn't get touched if left side is true. Left side of && (AndAlso) doesn't get touched if right side is false.

If you want both sides to get incremented, use | and &.

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

A logical operation is said to be short-circuiting if the compiled code can bypass the evaluation of one expression depending on the result of another expression. If the result of the first expression evaluated determines the final result of the operation, there is no need to evaluate the second expression, because it cannot change the final result. Short-circuiting can improve performance if the bypassed expression is complex, or if it involves procedure calls.

MSDN has tables that show how parts are "(not evaluated)".

Why touch part 2, if Part 1 pre-determines the outcome deterministically?

查看更多
还给你的自由
4楼-- · 2019-01-01 10:04

Huh?

If you're saying that && binds tighter than || (which is true), the expression is then equivalent to

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

Since || short-circuits, it needs to evaluate the left-hand side first.

If you mean that it ought to be equivalent to

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

The same is still true, since && also short-circuits, meaning the || needs to be evaluated first, which in turn makes ++x the first thing to evaluate.

查看更多
一个人的天荒地老
5楼-- · 2019-01-01 10:05

Unwind, R and others have explained what really happens. So let me just add:

The premise of your question is faulty. The fact that the && has higher precedence doesn't mean that operands that surround it must be evaluated before any operands in the expression with lower precedence. Even where the special case short-circuiting of || and && this wouldn't necessarily be so.

For example, consider a=b+c+d*e; * has higher precedence than +, but that doesn't mean that d*e must be evaluated before b+c. It just means that it must be evaluated before we add the product to the expression as a whole. A compiler could evaluate this expression as temp1=d*e, temp2=b+c, a=temp1+temp2 or it could evaluate temp1=b+c, temp2=d*e, a=temp1+temp2. Both would be equally valid.

With the short-circuiting behavior of || and &&, there are some additional restrictions placed on order of evaluation.


As a side note: In general I would avoid writing code like this. I can easily see another programmer trying to read this code getting confused about just when the increments will happen and when they won't. Well, maybe if you used real variable names it would not look so bizarre.

I do occasionally rely on short-circuiting preventing side effects. Like

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

I'm relying on the short-circuit to prevent reading if we're already at end of file, Or

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

I'm relying on the first test to short-circuit on false so I don't do the delete when I shouldn't. But these examples seem pretty straightforward to me, I'd hope any competent programmer would see what I'm doing. But when the examples get cryptic, I think it needs to be broken out. Consider

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

If lastDepositAmount() had a side effect, then if customerType is retail this side effect will never happen. I don't think that would necessarily be obvious to a reader. (Partly because the function name implies that it is retrieving data and not performing any update, and partly because there is no obvious relationship between the customer type and the amount of a deposit -- these sound like two independent things.) Admittedly, this is subjective. But when in doubt, choose simplicity and clarity over a trivial performance improvement. Always choose simplicity and clarity over "hey this is a way cool use of an obscure feature, anyone reading this will be impressed at how smart I must be to understand the language well enough to do this".

查看更多
刘海飞了
6楼-- · 2019-01-01 10:10

Precedence and order of evaluation are not the same thing, especially in this case. All precedence tells you is that the expression should be parsed as

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

IOW, it means that the expressions being OR'd together are ++x and (++y && ++z). It does not mean that (++y && ++z) will be evaluated before ++x.

For the logical operators || and &&, evaluation is always left-to-right (Online C Standard, Sections 6.5.13 and 6.5.14). For any expression

a || b

a will be completely evaluated; b will not be evaluated unless the result of a is 0 (think of a standing in for ++x and b standing in for ++y && ++z). Similarly, for any expression

a && b 

a will be completely evaluated; b will not be evaluated unless the result of a is not 0.

So for your case, the expression ++x is evaluated first. If the result is 0, only then will ++y && ++z be evaluated.

|| and && are special in that the order of evaluation is guaranteed to be left-to-right. That is not true of the bitwise or arithmetic operators. For example, in the expression

++x + ++y * ++z

the expressions ++x, ++y, and ++z may be evaluated in any order; all precedence tells you is that the result of (y+1) * (z+1) will be added to the result of x+1.

查看更多
登录 后发表回答