Strange operands to conditional operator

P

paulo

Can anyone please tell me how the C language interprets the following
code:

#include <stdio.h>

int main(void)
{
int a = 1;
int b = 10;
int x = 3;
int r;

r = (x > 0) ? a = b : 0; /* what does this statement mean */

printf("%d\n", r);

return r;
}

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, so the assignment of
b to a should not be performed until after the conditional operator has
been evaluated. So what value *should* be assigned to "r" - or is the
code meaningless ?

Thank you

Paulo
 
M

mark_bluemel

paulo said:
Can anyone please tell me how the C language interprets the following
code:

#include <stdio.h>

int main(void)
{
int a = 1;
int b = 10;
int x = 3;
int r;

r = (x > 0) ? a = b : 0; /* what does this statement mean */

printf("%d\n", r);

return r;
}

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, so the assignment of
b to a should not be performed until after the conditional operator has
been evaluated. So what value *should* be assigned to "r" - or is the
code meaningless ?

The order of precedence is not relevant to the question, IMHO (I'm
prepared to be shown wrong, of course).

The tertiary conditional operator (which I dislike, personally) must
take the form "expr1?expr2:expr3".

If expr2 contains an assignment, that's not going to wait until after
the condtional has been evaluated - the conditional can't be evaluated
until the the assignment takes place.

So after the line in question, "a" contains 10, as does "r".
 
R

Richard Bos

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.
 
F

Frederick Gotham

paulo:
r = (x > 0) ? a = b : 0; /* what does this statement mean */


More verbosely, it can be written as:

if (x > 0)
{
a = b;

r = a;
}
else
{
r = 0;
}

However, according to the language standard, the conditional operator
has higher precedence than an assignment operator, so the assignment of
b to a should not be performed until after the conditional operator has
been evaluated. So what value *should* be assigned to "r" - or is the
code meaningless ?


Recently enough, someone posted a link to a post by Chris Torek (I believe,
and apologies if I misspelled the name) which was very helpful in explaining
operator evaluation and so forth.
 
A

Arthur J. O'Dwyer

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 */
[big snip]
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.

I snipped most of an excellent explanation! However, you might have
gone a little overboard right here. At least, IMHO, you ought to have
added that it's not the value of 'b' that gets assigned to 'r' --- it's
the value of 'a', or possibly "the value of 'b', converted to a value
of the same type as 'a'".

If b,a,r are all of the same type, it doesn't really matter (AFAICT),
but if the types are respectively int, unsigned char, int, then it
could matter.
- If x<0, don't touch either a or b, and assign 0 to r.

-Arthur
 
K

Keith Thompson

paulo said:
Can anyone please tell me how the C language interprets the following
code:

#include <stdio.h>

int main(void)
{
int a = 1;
int b = 10;
int x = 3;
int r;

r = (x > 0) ? a = b : 0; /* what does this statement mean */

printf("%d\n", r);

return r;
}
[...]

Others have explained how the language rules apply to this code. I'll
make another point: this is bad code.

As a puzzle, there's nothing wrong with it, but I'd never write
something like that in production code. "a", "b", "x" and "r" are, in
most contexts, lousy variable names. (But "x" could be acceptable as
a parameter name for a mathematical function, and "r" might mean
"radius" if it's sufficiently clear from context.) Embedding
assignments within larger expressions is perfectly legal, but can lead
to confusion. The associations of operands with operators is not
ambiguous, but there's no good reason not to add parentheses to make
it clearer, so a reader doesn't have to consult the language grammar
to understand the code. (The compiler effectively does so, but that's
its job.)

r = ((x > 0) ? (a = b) : 0);

But I wouldn't use the "?:" operator at all here:

if (x > 0) {
r = a = b; /* chained assignments are clear enough, IMHO */
}
else {
r = 0;
}

And as long as we're simplifying things, the whole program reduces to:

#include <stdio.h>
int main(void)
{
puts("10");
return 10;
}

Finally, returning 10 from main() could have unpredictable results;
the only portable return values are 0, EXIT_SUCCESS, and EXIT_FAILURE.

Again, there's nothing wrong with the original code *as a puzzle*, and
there's nothing wrong with puzzles. Solving such puzzles is one of
the many things a good C programmer needs to know how to do (since we
all occasionally need to understand code written by someone who
thought he was "clever".) But another important skill is knowing not
to write such code in the first place.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top