views:

1713

answers:

6

Does the ANSI standard mandate logic operators to be short-circuited, in either C or C++?

I'm confused for I recall the K&R book saying your code shouldn't depend on these operations being short circuited, for they may not. Could someone please point out where in the standard it's said logic ops are always short-circuited? I'm mostly interested on C++, an answer also for C would be great.

I also remember reading (can't remember where) that evaluation order isn't strictly defined, so your code shouldn't depend or assume functions within an expression would be executed in a specific order: by the end of a statement all referenced functions will have been called, but the compiler has freedom in selecting the most efficient order.

Does the standard indicate the evaluation order of this expression?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";

Thanks in advance!

+2  A: 

If you trust Wikipedia:

[&& and ||] are semantically distinct from the bit-wise operators & and | because they will never evaluate the right operand if the result can be determined from the left alone

http://en.wikipedia.org/wiki/C_(programming_language)#Characteristics

Ben Alpert
Why trust wiki when we have a standard!
Martin York
+8  A: 

Yes, it mandates that (both evaluation order and short circuit). In your example if all functions return true, the order of the calls are strictly from functionA then functionB and then functionC. Used for this like

if(ptr && ptr->value) { 
    ...
}

Same for the comma operator:

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b());

One says between the left and right operand of &&, ||, , and between the first and second/third operand of ?: (conditional operator) is a "sequence point". Any side effects are evaluated completely before that point. So, this is safe:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

Note that the comma operator is not to be confused with the syntactical comma used to separate things:

// order of calls to a and b is unspecified!
function(a(), b());

The C++ Standard says in 5.14/1:

The && operator groups left-to-right. The operands are both implicitly converted to type bool (clause 4). The result is true if both operands are true and false otherwise. Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.

And in 5.15/1:

The || operator groups left-to-right. The operands are both implicitly converted to bool (clause 4). It returns true if either of its operands is true, and false otherwise. Unlike |, || guarantees left-to-right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to true.

It says for both next to those:

The result is a bool. All side effects of the first expression except for destruction of temporaries (12.2) happen before the second expression is evaluated.

In addition to that, 1.9/18 says

In the evaluation of each of the expressions

  • a && b
  • a || b
  • a ? b : C
  • a , b

using the built-in meaning of the operators in these expressions (5.14, 5.15, 5.16, 5.18), there is a sequence point after the evaluation of the first expression.

Johannes Schaub - litb
+7  A: 

Straight from good old K&R:

C guarantees that &&' and ||' are evaluated left to right -- we shall soon see cases where this matters.

John T
+20  A: 

Short circuit evaluation, and order of evaluation, is a mandated semantic standard in both C and C++.

If it wasn't, code like this would not be a common idiom

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

Section 6.5.13 Logical AND operator of the C99 specification (PDF link) says

(4). Unlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares equal to 0, the second operand is not evaluated.

Similarly, section 6.5.14 Logical OR operator says

(4) Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares unequal to 0, the second operand is not evaluated.

Similar wording can be found in the C++ standards, check section 5.14 in this draft copy. As checkers notes in another answer, if you override && or ||, then both operands must be evaluated as it becomes a regular function call.

Paul Dixon
Ah, what I was looking for! OK, so both evaluation order *and* short-circuiting are mandated as per ANSI-C 99! I'd really love to see teh equivalent reference for ANSI-C++, though I'm almost 99% it must be the same.
Joe Pineda
Hard to find a good free link for the C++ standards, have linked to a draft copy I found with some googling.
Paul Dixon
Martin York
Johannes Schaub - litb
I wish I could accept both Checkers and this answer. Since I'm mostly interested in C++, I'm accepting the other one, though have to admit this is superb, too! Thank you very much!
Joe Pineda
+25  A: 
Alex B
Didn't know short-circuiting wouldn't apply to overloaded logic ops, that's intesting. Can you please add a reference to the standard, or a source? I'm not distrusting you, just want to learn more about this.
Joe Pineda
Johannes Schaub - litb
jmucchiello
joe, yeah indeed that's what i was trying to say. of course, the implementation cannot determine whether evaluation order matters again or not. sorry for being so blurry :)
Johannes Schaub - litb
Joe Pineda
@Joe: but return value and arguments of the operator may change from boolean to something else. I used to implement "special" logic with THREE values ("true", "false" and "unknown"). Return value is deterministic, but short-circuiting behaviour is not appropriate.
Alex B
Joe Pineda
But complexity of compilers would grow, so probably only math applications would benefit. Probably my dream of stating my overloaded operators are deterministic, idempotent, etc. is better left for LISP-like languages with their lazy evaluation, memoization, etc.
Joe Pineda
+4  A: 

Be very very careful.

For POD types these are shortcut operators.

But if you define these operators for your own classes they are not shortcut. Because of this semantic difference in their usage under these different circumstances it is recommended that you do not define these operators.

For the operator && and operator || for POD types the evaluation order is left to right (otherwise short cutting would be hard :-) But for overloaded operators that you define, these are basically syntactic sugar to defining a method and thus the order of evaluation of the parameters is undefined.

Martin York
Operator overloading has nothing to do with the type being POD or not. To define an operator function, at least one of the arguments needs to be a class (or struct or union) or an enum, or a reference to one of those. Being POD means that you can use memcpy on it.
Derek Ledbetter
Martin York
Derek Ledbetter
I was using POD as (char/int/float etc) not an aggrigate POD (which is what you are talking about) and is usually refered to seprately or more explicitly becuase it is not a built in type.
Martin York