2

Let e1 and e2be 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?

perror
  • 7,071
  • 16
  • 58
  • 85
gormandy
  • 171
  • 5
  • 6
    @P0W: Remember short-circuit evaluation? – Keith Thompson Sep 12 '13 at 19:24
  • http://en.wikipedia.org/wiki/Short-circuit_evaluation – Adam Burry Sep 12 '13 at 19:27
  • 1
    What source are you using to learn C. Any C text book or tutorial should explain this in greater detail than we can explain here. (You can't learn C by asking about one operator at a time.) – Keith Thompson Sep 12 '13 at 19:29
  • We're using a subset of C, it's not very well documented unfortunately and I haven't gotten my hands on a C textbook yet – gormandy Sep 12 '13 at 19:31
  • Why are there 6 answers that say the same exact thing? – VoronoiPotato Sep 12 '13 at 20:19
  • Wouldn't a comment suffice? It doesn't seem constructive to dilute the answer pool simple because the standard wasn't quoted. – VoronoiPotato Sep 12 '13 at 20:32
  • @VoronoiPotato Comments are not answers, there is always the debate about [deleting comments](http://meta.stackexchange.com/questions/180325/help-us-figure-out-a-way-to-handle-the-explosion-of-comments-on-stack-overflow) over time which make me wary to put anything that I want to see stick around in a comment. – Shafik Yaghmour Sep 12 '13 at 21:38
  • 2
    @VoronoiPotato: In large part, it is because there were initial answers that were completely wrong and yet received votes up, so several people jumped in with correct answers. Even the answers that correctly stated the expressions were not equivalent omitted reasons why, so many people regarded the existing correct answers as unsatisfactory. StackOverflow permits people to edit answers, but some people are reluctant to do that, and some authors react poorly to editing of their answers. The currently top-voted answer was incomplete for a while and remains imperfect. – Eric Postpischil Sep 13 '13 at 00:57
  • @EricPostpischil In general I am hesitant to edit answers unless it is to improve style issues ... I won't generally add content unless the account is no longer around or has been inactive for a long time. I have seen enough bad reactions that I think it is not worth it. – Shafik Yaghmour Sep 13 '13 at 12:39
  • @VoronoiPotato: because many of us answered at the same time. Welcome to SO, check out http://stackoverflow.com/help – user7116 Sep 13 '13 at 13:12
  • I was here, same time would be a bit of a stretch... I think Eric's answer is more likely the honest one. – VoronoiPotato Sep 13 '13 at 14:51

5 Answers5

14

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.

Community
  • 1
  • 1
bobobobo
  • 64,917
  • 62
  • 258
  • 363
  • 2
    The nominal result is equivalent, but the behavior is not equivalent in C. If the first operand to `&&` is false, the second operand will not be evaluated, and any side effects it has will not occur. – Eric Postpischil Sep 12 '13 at 19:24
  • 7
    For example, `if (y != 0 && x / y == 3)` avoids dividing by zero. – Keith Thompson Sep 12 '13 at 19:25
  • Or if `e1` returns true, but has side effects that change what `e2` will evaluate to. – Joshua Taylor Sep 12 '13 at 19:31
  • 2
    Side effects are not the only problem; the second expression could have other behavior that is undesired if the first expression is false, such as dereferencing a null pointer. – Eric Postpischil Sep 12 '13 at 19:32
  • @EricPostpischil Well, people use / __rely on__ short cct evaluation in code such as `if( x && x->eval() )` because it has this behavior. If there were no such thing as short cct evaluation, it would be written as a nested `if( x ) { if( x->eval() ) }`. – bobobobo Sep 13 '13 at 01:14
  • @bobobobo: What is your point? I am not arguing against “short circuit“ behavior. Why did you revert my edit? This answer is incomplete without it: Side effects are not the only thing that makes `e1 && e2` different from `e2 && e1`. – Eric Postpischil Sep 13 '13 at 01:17
  • @bobobobo: @Eric is right, but you two seem to agree conceptually anyway. Maybe it is a misunderstanding on terminology: `if ( x && *x = 1 ...` if the second operand were executed with `x` being `NULL` that would be undefined behavior, not a side effect. Executing code that invokes UB makes the whole program erroneous and this doesn't fall under the "side effect" umbrella. Maybe @Eric should have said "undefined behavior" instead of "undesired". – LorenzoDonati4Ukraine-OnStrike Sep 13 '13 at 06:50
  • @LorenzoDonati: I used “undesired behavior” because I wanted to include implementation-defined behavior as well. – Eric Postpischil Sep 13 '13 at 15:42
  • @EricPostpischil Right! IDB is also to be taken into account. Useful clarification indeed. – LorenzoDonati4Ukraine-OnStrike Sep 13 '13 at 16:56
  • @EricPostpischil A situation like that sounds like incorrect code. – bobobobo Sep 13 '13 at 17:08
  • @bobobobo: You added an example of it to this answer. In `x && x->eval()`, `x->eval()` has undefined behavior if `x` evaluates to false. So the sentence that says the expressions are not equivalent if “if evaluation of `e1` or `e2` has side effects” is incomplete. It should include **all** undesired effects in `e2`, including undefined behavior and implementation-defined behavior. – Eric Postpischil Sep 13 '13 at 19:45
  • In `( x && x->eval() )`, `x->eval()` [won't execute if `x` evaluates to false](http://stackoverflow.com/a/628538/111307). – bobobobo Sep 13 '13 at 20:25
2

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

Freddie
  • 871
  • 6
  • 10
  • It would be noted that the `=` shown in this answer means “stands for”, not assignment. `e1` and `e2` are not actual identifiers used in the code; they are just symbols used to discuss what is in the code. – Eric Postpischil Sep 12 '13 at 19:38
  • @EricPostpischil True. If this was code, we'd get a segfault when attempting to evaluate the `e2` assignment. – Freddie Sep 12 '13 at 19:44
0

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.

user7116
  • 63,008
  • 17
  • 141
  • 172
  • Sometimes they're equivalent; sometimes they're not. I'd call that *not equivalent*. `+` and `*` yield the same result when both operands are `2`, but it doesn't make sense to say that they "*may be equivalent*". – Keith Thompson Sep 12 '13 at 19:28
  • @KeithThompson: I'll add "logically" if that suffices (not sure how swapping two operators applies to the OP's question; so I won't work in your example). – user7116 Sep 12 '13 at 19:29
  • Depends on how you add "logically", I suppose. In any case, other answers already cover this. – Keith Thompson Sep 12 '13 at 19:30
0

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.

frankfle
  • 111
  • 4
0

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.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740