Home > Software engineering >  What are the implications for the compiler of using a for loop with ternary statements as conditions
What are the implications for the compiler of using a for loop with ternary statements as conditions

Time:01-20

C Language, for-loop.

Instead of having two different loops that have identical printf statements, you can make a for loop that uses a boolean and ternaries to switch it forward or backward.

My question is, why is this bad form? Because it's unreadable? Does the compiler make two different loops, anyways?

Just curious as to what this means for the compiled result. Thanks.

Example:

for (int i = (forward == true ? 0 : 10);
    (forward == true ? i <= 10 : i >= 0);
    (forward == true ?   i : --i))
    printf(" %d", i);

Instead of:

// Forward
for (int i = 0; i <= 10;   i)
    printf(" %d", i);

// Backward
for (int i = 10; i >= 0; --i)
    printf(" %d", i);

EDIT:

I was given this interesting suggestion:

  for (int i = 0; i < 10; i  ) {
        printf(" %d", (forward ? i : 9-i));
  }

UPDATE:

After checking the assembly code using Godbolt, it is possible that the compiler will make one (complicated) or two (simplified) loops.

CodePudding user response:

Your existing code can be significantly simplified:

#include <stdio.h>
#include <stdbool.h>

int main(void) {
    bool forward = false;
    for (int i = 10*!forward; i <= 10 && i >= 0; i  = 2*forward - 1)
    {
        printf(" %d\n", i);
    }
    return 0;
}

  • int i = 10*!forward : Set i to 0 or 10, depending on forward
  • i <= 10 && i >= 0 : Keep going, as long as i is between 0 and 10
  • i = 2*forward - 1 : Update i by 1 or -1, depending on forward

When forward is set to false, the loop outputs:

 10
 9
 8
 7
 6
 5
 4
 3
 2
 1
 0

And when forward is set to true, the same loop outputs:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
 10

I believe that is what you wanted?

Where the behavior of the same loop could go either way, depending on forward := [true | false]

CodePudding user response:

C is just about perfectly general in the way it allows and evaluates expressions, so you can put virtually whatever jawbreaker expression you want into any (or all three) of the controlling expressions of a for loop (or anywhere else C expects an expression), and it should work.

I have used ?: in for loop headers, although as other answers have suggested, it can pretty rapidly get unreadable.

If I had to write a general, up-or-down loop such as you asked about, I'd probably do it like this:

int start, end, dir;

if(forward) {
    start = 0;
    end = 10;
    dir =  1;
} else {
    start = 10;
    end = 0;
    dir = -1;
}

for(int i = start; i != end; i  = dir) {
    …
}

For any of the loops we've discussed here (those you initially asked about, or in any of the answers), the performance is probably going to be virtually identical. Details like this have never mattered much, and with ever-faster CPUs and ever-more-aggressively-optimizing compilers, they make even less difference today. As always, if you really care, you'll have to perform careful measurements, and/or inspect the generated assembly code. Human predictions about performance at this level are rarely accurate or useful.

CodePudding user response:

why is this bad form? Because it's unreadable?

Yes it is bad because it is unreadable.

C does not required a single or two different loops. The emitted code is an implementation defined issue.

In general, with such micro optimizations consider:

Save your valuable time to deal with bigger issues.

CodePudding user response:

Compilers deal with complexity far better than humans. Yes, the first example is difficult to read.

Very much in the spirit of the answer given by @SteveSummit, the boundaries and "direction" can be clearly specified outside the loop.

The following alternative "knows" that there will be at least one value printed.

#include <stdio.h>
#include <stdbool.h>

int main( void ) {
    bool forward = false;
    int st = 0, inc = 1, end = 10;
    if( !forward ) st = 10, inc = -1, end = 0;
    do printf( " %d", st ); while( (st  = inc) != (end   inc) );
    return 0;
}
 10 9 8 7 6 5 4 3 2 1 0
  • Related