C Programming - comma operator within while loop
The comma operator evaluates both of its arguments in turn, throwing away the result, except for the last. The last evaluated expression determines the result of the entire expression.
i<=8,i++
- here the value of the expression is the value of i++
, which is the value of i
before being incremented. It's 0 so the loop immediately terminates.
i++,i<=8
- here the value of the expression is the value of i<=8
which is 0 only when i
is incremented to 9.
On a personal note: I think the second form, while somewhat comparable to a for loop, is less clear to the reader of the code than an actual for loop.
1 while ( condition )
2 statement;
3 more_code();
In the above code snippet, the statement
can be executed repeatedly as long as condition
is true
. On each iteration of the while loop, condition
is evaluated to either true
or false
. If it's false
, the while loop ends and execution continues beyond it's scope (in this case, line 4 with more_code()
.
We are usually accustomed to enclosing parts of code that we want to be executed in loop with curly brackets {
and }
, but that is not mandatory. If we do not do so, the looping code will consist of single statement, the one immediately following the while
part.
It could actually be argued that the more common situation, where we combine while
with curly brackets enclosed block of code could be interpreted as providing this block of code in place of a single statement, with braces providing information that the block should be treated (by compiler analysing it's relation with preceding and following code) as if it was a single statement.
However, as it is perfectly valid to provide a single statement, not a usual block of code, it's worth to understand that there is a valid statement that is empty. We get an empty statement by typing a semicolon without preceding it with a code causing anything. So following is perfectly valid:
1 code;
2 ; // empty statement
3 ; // another empty statement
or in fact this:
1 code;; // a "code" statement followed by empty statement in the same line
The while( condition )
part is not terminated with a semicolon, so if it's supposed to control some actual code (apart from condition
), it should not be followed by a semicolon. If it is immediately followed by a semicolon, that semicolon will constitute (and be so interpreted by compiler) an empty statement, so the looping code will be empty. If that's unintended, then the code we wanted to be looped, whether a block of code or a statement, will not be looped, but rather executed once, after (and if) loop ends.
1 int a = 0;
2 while ( a < 3 ) ; // Next line is not part of loop - only the empty statement this semicolon creates is. This loop is infinite, or in other words it will never end.
3 a++; // This won't be executed even once.
4 printf("This never happens.");
(It's worth realizing that lines are only important for us, humans, in C. Lines and indentation can be misleading if they represent the intentions of programmer, when he failed to write the code functioning as he wanted it to.)
Therefore what happens in both snippets from the question, is we get condition
evaluated continuously until it yields false
. To understand what's going on we need to examine the way comma operator works.
(Note, while comma as a character can be used with a completely different meaning in various places in C - I can think of function declarations, definitions and calls - in this case comma character is part of condition, therefore it acts as an operator - something akin to +
or %
operators.)
expression1 , expression2
Comma operator causes expression1
to be evaluated first, then expression2
, and returns the value of expression2
.
Upon every evaluation of condition, we will thus evaluate both expressions, (in this case both being operands, i++
and i<=8
), then consider value of the right one as result of comma operand itself, and thus as value of our condition. So the loop will keep repeating as long as the right operand resolves as true
.
While usually we use condition to control the execution of loop, often, as in this case, condition
may have "side" effects (intentional or unintended). In our case variable i
is affected by every evaluation of condition
: it is increased by one.
Our example differs only in order of operands of condition
, therefore pay attention to the right operand which really controls the execution of the loop.
Let's examine the second example first. In this case we have condition i++, i<=8
. This means upon every evaluation we first increase i
, then check if it's less than or equal to 8. So on first evaluation of condition we will increase i
from 0 to 1 and conclude that 1<=8, so the loop continues. The loop so constructed will break when i
becomes 9, ie. on the 9th iteration.
Now as for the first example, the condition is i<=8, ++i
. Since comparison has no side effects, that is we could perform any number of comparisons in any order and if that's the only thing we did, that is if we did not perform any other action in a way or order dependent of results of the comparisons, those comparisons would do absolutely nothing. As is in our case, we evaluate i<=8
which evaluates to true
or false
, but we make no use of this result, just proceed to evaluating the right operand. So left operand absolutely doesn't matter. Right operand, on the other hand, has both a side effect and it's value becomes value of entire condition. Before each loop iteration we check if i++
evaluates to true
or false
.
i++
is a unary operator of post-incrementation. It returns value of i
then increases it by one (the difference between i++ and ++i is subtle but crucial in cases like this one). So what happens is we first check whether i
is true
or false
, then i
is increased by one.
In C there is no boolean
type. Integers are considered to be true
if they have a non-zero value.
So upon first evaluation of i++
we get 0, that is false
. This means the loop is broken without even a single iteration. However it does not break evaluation of i++
, which causes i
to increase by one before we're done with the loop and execution proceeds beyond it. So once we're done with the while loop, i
is already 1.
If we want to be very precise in our understanding, the part where we take the result of evaluating entire condition happens, chronologically, after we are finished executing any code involved in this evaluation. So we first memorize that i
was 0 at the point we got toward i++
part, then we increase i
by one, and then we are finished executing condition
, so we provide value of 0 to the code that decides if we should do another (in this case first) iteration or jump beyond looping part and move on. This is exact reason why everything within condition will actually happen even though the fact that loop will end was already determined: it was determined, but it was not checked and acted upon until condition
finishes executing.