Different results when using increment operator (arr[i++] vs arr[i]; i++;)
Asked Answered
J

3

13

I can't get my head around why the code below is not working as expected:

#include <stdio.h>

int main() {
    int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
    while (i < size && oneOrZero[i++]);
    if (i == size) printf("All ones"); else printf("Has a zero");
}
Terminal: All ones.

When incrementing the index inside the loop makes the code run as expected:

#include <stdio.h>

int main() {
    int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
    while (i < size && oneOrZero[i]) {i++;}
    if (i == size) printf("All ones"); else printf("Has a zero");
}
Terminal: Has a zero.

Could someone explain the difference between these two?

Jayejaylene answered 12/10, 2020 at 8:54 Comment(6)
Read closely the documantation of the && operator. If the left hand expresion is 0, then the right hand expression will not be evaluated at all. Read this, andd/or google c operator short circuitDisfavor
As @Disfavor already said the && and || evaluation is optimized so that if the first condition is False (0) then the second condition is not needed to be evaluated because the whole would be False (0) anyway. And the similar thing happens when the first condition is True (1) on || operator. When you i++ is on the second condition it isn't always evaluated like when it's on while block.Comfit
as @pmg said, when value of i is 8, oneOrZero[i] returns 0 but, before loop ends, i is incremented so, in the next test i value is 9Highlight
@pmg comment was correct, this does not relate to operator short circuit.Boschvark
Try to write code as readable as possible and bugs like these tend to disappear. for(i=0; i<size; i++) { if(oneOrZero[i] == 0) { break; } You can even introduce a bool found variable for extra readability: for(int i=0; i<size && !found; i++) { if(oneOrZero[i] == 0) { found=true; }Sunset
The moral of the story: number_of_bugs_caused_by_post_increment++;. Irony intended.Literality
B
17

In the first code, when i is 8, oneOrZero[i] will evaluate to false because oneOrZero[8] == 0, but i will be incremented to 9 anyway, the increment is not dependent on the truthiness of the expression, it will happen as many times as the expression is evaluated.

So naturally when i == size is evaluated it's 9 == 9, this is, of course, true, therefore "All ones" will be printed giving you the wrong output.

In the second code i is incremented inside the body of the conditional expression, this means it will only be incremented if the condition is met, so when i is 8, oneOrZero[i] will evaluate to false and i is not incremented, retaining its 8 value.

In the next line statement i == size will be 8 == 9 which is false and "Has a zero" will be printed, giving you the correct output.

Boschvark answered 12/10, 2020 at 9:29 Comment(0)
S
7

This is a typical off-by-one error when one uses a iteration index i also for a check (comparison with size). No worries, it happens to almost everyone, all the time.

The problem is that, even though the condition failed, we already changed the result (i) in oneOrZero[i++]. Our second variant doesn't fall into this trap, as the condition and the index increment are decoupled.

We can replicate that behavior with a simpler example:

#include <stdio.h>

int main() {
    int i = 0, size = 1, oneOrZero[] = {0};
    while (i < size && oneOrZero[i++]);
    if (i == size) printf("All ones"); else printf("Has a zero");
}

Now, let's check the condition by hand:

  1. i < size is fine, so we continue to evaluate the right-hand side.
  2. i++ increments i to 1 (aka size)
  3. oneOrZero[0] is 0, thus the condition fails

After this single iteration, i == size, and we print All ones.


Compare this to the other variant:

int main() {
    int i = 0, size = 1, oneOrZero[] = {0};
    while (i < size && oneOrZero[i]) {i++;}
    if (i == size) printf("All ones"); else printf("Has a zero");
}

Again, we check the condition:

  1. i < size is fine
  2. oneOrZero[0] == 0, so we stop.
  3. i never gets incremented

Thus i < size and we print Has a zero.


Note that it's possible to change the condition into

int i = -1;

while(++i < size && oneOrZero[i]);

but that needs careful documentation.

Spradlin answered 12/10, 2020 at 9:30 Comment(0)
C
0
   #include <stdio.h>
        
        int main() {
            int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
            while (i < size && oneOrZero[i++]);
            if (i == size) printf("All ones"); else printf("Has a zero");
        }

The above code executes till i = 8 and the first condition i < size that is 8 < 9 but the second condition oneOrZero[8] is false. and anyway i will be incremented to 9. 9 == 9 so it will print "All ones" will be printed giving you the wrong output.

#include <stdio.h>

int main() {
    int i = 0, size = 9, oneOrZero[] = {1,1,1,1,1,1,1,1,0};
    while (i < size && oneOrZero[i]) {i++;}
    if (i == size) printf("All ones"); else printf("Has a zero");
}

The above code executes till i = 8 and evaluates to i < size and 8 < 9 but oneOrZero[i] oneOrZero[8] = 0 and evaluates to false and comes out of the loop and i == size 8 == 9 and prints Has a zero will be printed, giving you the correct output.

Calvillo answered 28/10, 2020 at 11:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.