Let e1
and e2
be two boolean expressions. Then, is e1 && e2
equivalent to e2 && e1
in C ?
My intuition tells me yes. By simple logic A & B
is equivalent to B & A
. Is this true in C as well?
Let e1
and e2
be two boolean expressions. Then, is e1 && e2
equivalent to e2 && e1
in C ?
My intuition tells me yes. By simple logic A & B
is equivalent to B & A
. Is this true in C as well?
Logically, yes. e1 && e2
will never have a different result than e2 && e1
from a logical standpoint.
But from a code standpoint, if evaluation of e1
or e2
has side effects, then no, they are not completely equivalent.
Specifically, sometimes people use functions in a chain of &&
.
if( isHungry() && hasFood() ) eat() ;
Usually you would want to put the cheapest to evaluation condition first, and only check the 2nd condition if the first condition held up as true. Short circuit evaluation is what guarantees this will happen. If the 1st condition in an AND logical operation is false, then the 2nd statement isn't even evaluated, because the end result won't be TRUE anyway (FALSE && (TRUE && TRUE && TRUE && TRUE)) == FALSE)
.
In other words you wouldn't bother checking the fridge if you're not even hungry.
Sometimes people rely on the fact that short circuit evaluation will happen, and write code that depends on it. This type of thing is shown in the comments below:
if( x && x->eval() ) { /* do something */ }
So, say x=NULL
above. What will happen? An attempt to deference a null pointer? Should it be written as
if( x )
{
if( x->eval() )
{
// do something
}
}
I said heck no! Short circuit evaluation means, if x==0
, the right operand to the &&
operator would not even be looked at in if( x && x->eval() )
. x->eval()
would not be evaluated, and no null pointer exception would occur.
The other thing you have to watch out for is if the functions you run in the if statement have a side effect, such as incrementing a counter of some sort. So, if hasFood() has an internal counter increment such as fridgeCount++
.
In the even of short cct evaluation, the function will not even run, so any side effects you are expecting to happen when you hit the if statement, might not happen.
The OR operator also has short circuit evaluation, only in if( e1 || e2 )
, the statement is short circuited to TRUE if e1
turns up to be TRUE (then e2 is not even evaluated, the whole expression is considered as TRUE
).
This behavior is true of C/C++, Java, JavaScript, PHP, Python, and many other languages.
Yes, but the evaluation is different. Let me point out an example where this matters.
e1 = (pStruct == NULL)
e2 = (pStruct->value != 0)
e1 && e2
is acceptable, but e2 && e1
will cause a segfault if pStruct
is NULL
because &&
is evaluated left to right
If e1
and e2
are expressions without side effects and they are both valid independently, then yes this will be equivalent.
In practice, they may not produce equivalent results even if both expressions are without side effects due to short circuiting behavior:
// given some array X of 10 members
if (i < 10 && x[i] < 5) {
//... this is not 'equivalent' to:
if (x[i] < 5 && i < 10) {
// ... but obviously:
if (i < 10 && z > 15) {
// ... is equivalent to:
if (z < 15 && i < 10) {
So, for the general case of any boolean expression e1
and e2
: no, they are not equivalent.
For a given case: yes, they may be logically equivalent.
Yes and no. But 99% (actually, probably much, much more) of the time, yes.
If you're only talking about the result, then yes. However, if you're expressions contain an functions or assignment (or anything that can change the state of something), then you should know that C is clever about how it will evaluate the arguments to &&.
If the first expression to && (the left value) is false, the second expression (the right value) will NOT be evaluated.
For example, it is safe to do something like:
struct some_struct *x = NULL;
if (x && x->some_struct_value) {
// Some code here
}
Since the left value x
is evaluated as false
, the right value x->value
, which would normally crash your application, will not be evaluated, and your application will not crash.
Another example would be something like this:
int always_returns_false() {
return 0;
}
int increment(*int x) {
(*x)++;
return 1;
}
int main() {
int my_int = 0;
if (always_returns_false() && increment(&my_int)) {
// Stuff! (Or no stuff, doesn't matter!)
}
return my_int;
}
If you switch around the expressions in the conditional on 11, you could get a return status of either 0, or 1.
So, while logically, (e1 && e2) == (e2 && e1)
, functionally, in C at least, they can cause different actions and outcomes.
e1 && e2
may not be equivalent to e2 && e1
because of short circuit evaluation, so if these expressions have side effects this can cause different behavior. the C99 draft standard in section 6.5.13
Logical AND operator paragraph 4 says:
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.
So the second operand will not be evaluated if the first operand is equal to 0
. So let's say that e1
evaluates to 0
but e2
evaluates to a non-zero value then in this case:
e1 && e2
then e1
will be evaluated but e2
will not but in this case:
e2 && e1
both e1
and e2
will be evaluated.