Edit:
What is the real groups of the expression “3<8 ? (9<6 ? 7 : 5) : 2>0 ? 4 : 1” and the meaning of non-associative in PHP?
has been offered as a duplicate, but that concerns PHP, not C.
While building some test cases for a small program, I introduced a bug into the conditional part of a for loop, like this:
for(int row = 0; row < (mode == TEST) ? NO_OF_TESTS : ROWS; row++){}
(I know one should pull that out of the for
loop, but that does not change the problem.)
This lead to a segmentation fault by overrunning the end of an array as the above results in an infinite loop.
Of course, the fix was easy enough:
for(row = 0; row < ((mode == TEST) ? NO_OF_TESTS : ROWS); row++)
// ^ ^
But I am more interested in the way the faulty implementation behaved.
Here is a complete piece of code (does not make sense in itself, as it is ripped out of context, but it demonstrates the problem).
#include <stdio.h>
#include <stdlib.h>
#define TEST 0
#define INTERACTIVE 1
#define ROWS 2
#define NO_OF_TESTS 3
#define MAX_FRUIT_LEN 50
int main(void)
{
char test_cases[NO_OF_TESTS][MAX_FRUIT_LEN] =
{{"Orange"},
{"Apple"},
{"Pineapple"}};
int mode = TEST;
int row = 0;
//This fails - but in a strange way
//Uncomment this `for` loop and comment the other one to see the effects
//for(int row = 0; row < (mode == TEST) ? NO_OF_TESTS : ROWS; row++)
//With the parantheses, obviously, it works.
for(row = 0; row < ((mode == TEST) ? NO_OF_TESTS : ROWS); row++)
{
printf("Working:\tIn row %d: Mode: %d condition_eval: %d\n"
, row , mode, row < ((mode == TEST) ? NO_OF_TESTS : ROWS));
printf("Not Working:\tIn row %d: Mode: %d condition_eval: %d\n"
, row, mode, row < (mode == TEST) ? NO_OF_TESTS : ROWS);
printf("Row: %d \tFruit Name: %s\n",row, test_cases[row]);
}
printf("\nTerminating conditional evaluation (at row %d):\n", row);
printf("Working:\tIn row %d: Mode: %d condition_eval: %d\n"
, row , mode, row < ((mode == TEST) ? NO_OF_TESTS : ROWS));
printf("Not Working:\tIn row %d: Mode: %d condition_eval: %d\n"
, row, mode, row < (mode == TEST) ? NO_OF_TESTS : ROWS);
return 0;
}
Looking at the output and the (wrong) conditional
row < (mode == TEST) ? NO_OF_TESTS : ROWS
it appears that the compiler interprets this as:
(row < (mode == TEST)) ? NO_OF_TESTS : ROWS
// ^ ^
The question is: Why?
This expression:
(mode == TEST)
could be interpreted as either being the right operand to the <
operator, or as the
left operand to the ?
operator. (But not both at the same time, I guess.)
Which rules apply? Is it a matter of operator precedence? Do sequence points play a role? What is the order of evaluation, and why?
I'm quite confused; any help is greatly appreciated.