paulo said:
Can anyone please tell me how the C language interprets the following
code:
r = (x > 0) ? a = b : 0; /* what does this statement mean */
The statement containing the conditional operator is the puzzle.
If it was written as "r = (x > 0) ? (a = b) : 0;" there wouldn't be a
problem.
However, according to the language standard, the conditional operator
has higher precedence than an assignment operator,
This is misleading...
so the assignment of b to a should not be performed until after the
conditional operator has been evaluated.
....and this is, in any case, not a correct conclusion to draw from _any_
question of preference in C.
There is, strictly speaking, no precedence in C. There is a syntax,
which specifies what an expression can look like. If you take all the
rules which specify the various kinds of expression, you end up with
something which looks suspiciously like the traditional precedence
table, but this table is a simplification of the real rules.
In the case of the above statement, the rule for a conditional
expression is:
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
and that for an assignment expression is:
assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
Add these together, and you'll see that in
a = b ? c : d;
this must parse as an assignment expression, of which the right-hand
part is also an assignment expression, which falls through to a
conditional expression. IOW, this parses as
a = (b ? c : d);
OTOH, in your case, which schematically is
a ? b = c : d;
the parse for the assignment expression cannot involve the : from the
surrounding conditional expression, because there is nothing in the
grammar which _can_ produce such a half-conditional. The only possible
parse is
a ? (b = c) : d;
This is complicated by this whole expression being inside another
assignment expression, but when you go through the grammar, you must end
up with the parse you quoted above yourself: the expression without the
extra parens is required to be parsed as the one with the parens. More
precisely yet, the statement
r = (x > 0) ? a = b : 0;
parses like
r = ((x>0) ? (a=b) : 0);
Now for the second part: order of evaluation. The order of evaluation of
sub-expressions in a C expression is normally free. In the expression
a+b, the implementation is free to fetch a first, b first, both in
parallel, or however it chooses; as long as the addition is performed
correctly, it does not matter.
This can be a trap for the unwary: for example, in push(pop() - pop()),
there is no guarantee which of the two pop()s is called first, and
therefore no guarantee that the number which is pushed is the top of the
stack minus the second stack member, or vice versa. Similarly, in
func1() + func2()*func3(), the fact that * binds more closely than +
does not mean that func2() and func3() must always be called before
func1(). Nor, of course, the reverse.
There is one thing, though, which is an exception to this freedom, and
that is sequence points. There are certain spots in a C program at which
every computation and side-effect must[1] be finished, and the next set
started. The obvious one is the end of a full expression, e.g. the end
of a whole expression statement, or the controlling expression of an if
statement; others are just before a function is called, after its
arguments have been evaluated (but _not_ between two argument
evaluations), just before a library function returns, and after the
first operand of certain operators.
That last one is significant, here. One of these operators is &&; there
is a sequence point between the first and second operand of any &&
operation. This is useful, for example, when you do if (ptr && *ptr<x),
because it means that the program must first evaluate ptr; the
definition of && also says that if ptr is 0, *ptr won't be evaluated,
and you won't have a segfault.[2] There is a similar rule for the
logical or operator, and for the comma.
You can see it coming: yes, the same rule goes for the ?: operator.
There is a sequence point after the first operand. This, added with the
rest of the definition of ?:, means that this (or something with the
exact same effect) happens:
- _First_, the first operand is evaluated. In your case, x is compared
to 0.
- Then, the decision is made which of the two other operands to
evaluate.
- And only then is _either_ the second _or_ the third operand evaluated.
Never both. And whichever is evaluated, that's only done after the
first operand is finished.
However, this is because ?:, like &&, || and , is special, and has an
extra sequence point and a rule about the order in which the operands
are evaluated. It is not because of precedence. This doesn't hold true
with operators like * and +, which also have different "precedence", but
do not have the special sequence point rules.
In toto, the statement
r = (x > 0) ? a = b : 0;
means this:
- Evaluate (x>0).
- If x>0, assign the value of b to both a and r. Note that there is no
sequence point between the _second_ operand of ?: and its surrounding
expression (here, the assignment to r), so the order in which b gets
assigned to a and r is unspecified.
- If x<0, don't touch either a or b, and assign 0 to r.
Richard
[1] FSVO "must". If the compiler can prove that the program cannot or
will not detect the difference, it can shuffle as it likes. If it
cannot prove this, however, it must adhere to the prescribed order.
[2] Again, if it can be proved that you can't tell the difference - for
example, if the compiler knows that this OS doesn't have memory
protection, and reading from a null pointer doesn't do anything, it
can ignore this rule. But _only_ if it makes no difference.