0

What is the computation order of the equal priority operands in C / C++ ?

For example in following piece of code:

if ( scanf("%c", &ch_variable) && (ch_variable == '\n') )

Can I be sure that 1st expression inside the IF statement is performed before the 2nd (i.e. the value of ch_variable compared, is a newly scanned one)?

Or is it somehow decided by compiler? And if so, how this decision is being made?

BTW, I usually use the following flags for compilation:

-std=c99 -O0 -pedantic -Wall -ansi

5 Answers5

1

Can I be sure that 1st expression inside the IF statement is performed before the 2nd (i.e. the value of ch_variable compared, is a newly scanned one)?

Yes - the first expression (the scanf call) is evaluated first, and what's more the second doesn't happen at all if the scanf call returns 0 - see below. That's short circuit evaluation.


Broader discussion.

Read about the operator precedence at cppreference.com

Summarily - operators are arranged in groups with well-defined relative precedence (e.g. '*' has higher precendence than +, as per usage in mathematics), and left-to-right or right-to-left associativity (e.g. a + b + c is left associative and evaluated as (a + b) + c, but a = b = c is right-associative and evaluated as a = (b = c)).

In your code:

if (scanf("%c", &ch_variable) && (ch_variable == '\n') )

The ( and ) work as you'd expect - overriding any implicit precedence between && and == (but in this case the precedence is the same). && is therefore uncontested, and as a short-circuit operator it ensures its left argument is converted - if necessary - to boolean (so if scanf returns 0 it's deemed false, otherwise true), then if and only if that's true does it evaluate the right-hand-side argument, and only if they're both true does the if statement run the following statement or {} statement block.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • The question "Can I be sure that 1st expression inside the IF statement is performed before the 2nd" doesn't have anything to do with operator precedence, but rather the order of evaluation of the operands of &&. – Lundin Aug 07 '14 at 09:17
  • @Lundin: true - the broadening of the answer to discuss operator precedence is so that the OP knows even `if (scanf("%c", &ch_variable) && ch_variable == '\n')` would have worked just fine, and can reason about adding tests like `scanf(...) == 1` without parenthesis around that comparison. I've restructured the answer for - hopefully - more clarity. – Tony Delroy Aug 07 '14 at 09:26
1

This has nothing to do with "priority" (operator precedence), but with the order of evaluation of sub-expressions.

The && operator is a special case in C, as it guarantees order of evaluation from left to right. There is a sequence point between the evaluation of the left operand and the right operand, meaning that the left operation will always be executed/evaluated first.

Many C operators do not come with this nice guarantee, however. Imagine the code had been like this:

if ( (scanf("%c", &ch_variable)!=0) & (ch_variable == '\n') )

This is obfuscated code but it logically does the same thing as your original code. With one exception: the & operator behaves as most operators in C, meaning there are no guarantees that the left operand will get evaluated before the right one. So my example has the potential of evaluating ch_variable before it has been given a valid value, which is a severe bug.

The order of evaluation of such sub-expressions is unspecified behavior, meaning that the compiler is free to evaluate any side first. It doesn't need to document what it will do and it doesn't even need to pick the same side consistently between compilations, or even pick the same side consistently throughout the program.

The language was deliberately designed this way to allow compilers to optimize the code in the best possible way, from case to case.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • "order of evaluation of such sub-expressions is *unspecified behavior*, meaning that the compiler is free to evaluate any side first" - under 1.9/15, are they not *unsequenced* (and potentially overlapping), rather than *unspecified* (which is used by the Standard 1.9/13 to elaborate the *indeterminately sequenced* situation)? – Tony Delroy Aug 07 '14 at 09:57
  • @TonyD I don't have access to the C++ standard, so I have no idea what that means. But the C++11 and C11 standards really messed up this text to a unreadable mess. C99 sums it up in a perfectly clear way (C99 6.5): `"Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored."` – Lundin Aug 07 '14 at 10:18
  • `"The grouping of operators and operands is indicated by the syntax. Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified."` – Lundin Aug 07 '14 at 10:18
  • The above was perfectly clear text. But the C*11 committees just had to muck it up. – Lundin Aug 07 '14 at 10:20
  • 1
    The committee had to deal with multi-threading, this is why it introduced the concept of sequenced before/sequenced after/unsequenced/indeterminably sequenced as a more general model than just sequence points. Anyway, +1, the only answer containing the phrase "sequence point". – mafso Aug 07 '14 at 11:31
0

Yes, absolutely, anything involving && and || (except if you use operator&& or operator|| - which is one of the main reasons NOT to use these operators) is "strict short cutting" - in other words, if the overall outcome of the result can be determined, the rest is not evaluated, and the order is strictly left to right - always, by the language standard. [Of course, if the compiler can be SURE it's completely safe, it may reorder things, but that is part of the "as-if" definition of the standard - if the compiler behaves "as-if" it is doing it the way the standard says].

Beware that:

 if(scanf("%c", &ch_variable) && scanf("%c", &second_variable))
 {
   ... 
 }
 else
 {
    ...
 }

may not have set "second_variable" at all in the else part, so it's unsafe to use it at this point.

I would aos use scanf("%c", &ch_variable) > 0 instead - as it could return -1 at EOF, which is true in your case, without an intermediate 0 return value...

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 1
    "`if(scanf("%c", &ch_variable) && scanf("%c", &second_variable))` ...may not have set "second_variable" at all in the else" - unless I'm very much mistaken `scanf` may not have set `ch_variable` either - it's not just the short-circuit evaluation at plan, but the behaviour of `scanf` when it `EOF`s without finding sought-after input. – Tony Delroy Aug 07 '14 at 09:19
0

It's guaranteed that the first expression is evaluated before the second one.

See Is short-circuiting logical operators mandated? And evaluation order? for a citation of the standard.

Note that if you overload the && operator, the whole expression is equivalent to a function call. In that case both expressions will be evaluated unconditionally (i.e. even if the first expression would be some "falsy" value...).

Community
  • 1
  • 1
MartinStettner
  • 28,719
  • 15
  • 79
  • 106
0

The order that the operands are evaluated is defined in this case, and it is left-to-right.

This goes for both C and C++.

For a reference of this, see for example page 99 of the C standard: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf.

Hence, in terms of order-of-evaluation, your code will do what you want it to. But it does have some other problems; see the post comments for this.

Carl
  • 937
  • 10
  • 21
  • 4
    This is wrong. For most operators, including the bitwise ones, evaluation order is not defined. It is defined for `&&` and `||`. – interjay Aug 07 '14 at 08:58
  • There is no guarantee that `a` will be evaluated first in `a & b`. So for example, `(x = 1) & x` results in undefined behavior. – interjay Aug 07 '14 at 09:05
  • 2
    Carl: associativity is not the same as evaluation order. Evaluation order is only defined for expressions with lazy evaluation (out of necessity!). In other cases the optimiser may rearrange the evaluation order. – Klas Lindbäck Aug 07 '14 at 09:05
  • Klas is correct... further, the code for non-sequenced evaluations may be interleaved. – Tony Delroy Aug 07 '14 at 09:13
  • You are absolutely correct. I have now read the standard. I have edited my answer. (And by mistake upvoted the comment by interjay, which was totally correct, but doesn't apply now that the post is edited... thanks anyway for pointing this out.) – Carl Aug 07 '14 at 09:14
  • 1
    I don't think there's any case where the evaluation order is defined as right-to-left. It's either unspecified (common case) or left-to-right (`&&`,`||`,`,`). – MSalters Aug 07 '14 at 09:44