Is it guaranteed to parse as a "(a ? b : (c ? d : e))"? - c

Suppose, If I use ternary operator like this: a ? b : c ? d : e
Code :
#include <stdio.h>
int main()
{
int a=1,b=2,c=3,d=4,e=5;
printf("%d\n", a ? b : c ? d : e);
return 0;
}
Gcc and Clang Give an output 2.
Questions:
Is it guaranteed to parse as a (a ? b : (c ? d : e))? or
Is it unspecified behaviour?
What the C standard says about it?

The syntax for the ternary operator, also known as a conditional expression, is defined in section 6.5.15 of the C standard as follows:
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
Because a "condition-expression" is not a "logical-OR-expression" (read: the logical OR operator has higher precedence) this prevents a ? b : c ? d : e from being parsed as (a ? b : c) ? d : e. This also means that the operator is right-to-left associative with itself. Therefore it gets parsed as a ? b : (c ? d : e).
For more details, you can find the operator precedence rules here. While the standard is the authoritative source, this table lists the rules in a manner that's easier to understand.
As it can be difficult for people to remember the full set of precedence rules, and because different languages sometimes have differing precedence rules, it is best to be explicit about the ordering of operations and use parenthesis to make your intentions more clear to the reader.

Related

"error: lvalue required as left operand of assignment" in conditional operator

I'm new to C and today I learnt "?" operator which is the short type of if-else statement. However, when I execute this code:
int b;
int x;
b=3<2?x=12:x=34;
I get an error "error: lvalue required as left operand of assignment". I don't understand why it happens. Process in my mind is that the program first assigns 34 to x, then it assigns value of x,which is 34, to b.
On the other hand, I can use the statement as
int b;
int x;
b=3<2?x=12:(x=34);
without any errors. I looked to my book but nothing helped. Why can't I use the first statement? What is my computer trying to do?
Thanks...
+1 for interesting question - it highlights two differences between C++ and C.
(1) The evaluation rules for ternary expressions are different in C and C++
C++ parses as follows
logical-OR-expression ? expression : assignment-expression
It is therefore parsing your statement by matching assignment-expression to x=34
b = 3<2 ? x = 12 : (x = 34);
But C parses like this
logical-OR-expression ? expression : conditional-expression
x = 34 is not a conditional-expression so your statement gets parsed like
b = (3<2 ? x = 12 : x) = 34;
(2) The conditional operator in C++ can return an lvalue, whereas C cannot. Hence, the following is legal in C++ but not in C:
b = (3<2 ? x = 12 : x) = 34;
Verified on ideone.com for C and C++ compilers.
See also these links
Errors using ternary operator in c for diff between C and C++ ternary operator
Conditional operator differences between C and C++ for diff in lvalue rules

Order of evaluation for conditional operator

It is known that both assignment = and conditional ?: operators have right associativity. In following code sample:
#include <stdio.h>
int main(void)
{
int a, b, c, d;
a = b = c = d = 1;
1 ? a++ : b ? c++ : d;
printf("%d %d %d %d\n", a, b, c, d);
return 0;
}
the assignment:
a = b = c = d = 1;
is equivalent to:
a = (b = (c = (d = 1)));
and correspondingly:
1 ? a++ : b ? c++ : d;
is the same as:
1 ? a++ : (b ? c++ : d);
What the Standard say about this last case? Does it guarantee that such combined expression is evaluated from left to right (so the c++ part is not evaluated), just as opposite to assignment?
The evaluation order of ?: is guaranteed: the first operand is evaluated first, then evaluate the second or the third operand depending on whether the first operand is true.
You are mainly confused about the relationship between operator precedence/associativity and order of evaluation. They play different roles. The former decides how operators are grouped, while the latter decides which sub-expression is evaluated first.
Consider the expression a * b + c * d, the precedence rule means it's equivalent to (a * b) + (c * d). But is it guaranteed that the compiler will evaluate a * b before c * d? The answer is no, in this example, the operator + doesn't guarantee the order of evaluation.
The conditional operator ?: is one of the few operators that do have a specified order of evaluation. (The rest are &&, ||, and ,).
In your example
1 ? a++ : b ? c++ : d;
1 ? a++ : (b ? c++ : d);
are always equivalent, in both expressions, 1 is evaluated first, and since it's true, a++ is evaluated next, the end.
Associativity and precedence do not define order of evaluation. These concepts are completely unrelated. Order of evaluation in C is defined by sequencing rules, not by precedence or associativity.
It is true that a = b = c = d = 1; is associated as a = (b = (c = (d = 1)));, but that does not mean that d = 1 should be evaluated first, especially in C language, where assignment operator evaluates to an rvalue.
Associtivity simply says that c should receive value 1 converted to the type of d ("as if" it was read from d). But that does not mean that d = 1 should be done first. In your example all variables have the same type, which means that the whole thing is equivalent to a = 1; b = 1; c = 1; d = 1; in absolutely any order. There's no sequencing inside a = b = c = d = 1 expression.
The same logic applies to ?: operator. Its associativity simply tells you which operand belongs to which operator. And the grouping is indeed 1 ? a++ : (b ? c++ : d);. But associativity does not tell you anything about the order of evaluation. Order of evaluation in ?: operator is defined separately and independently: the condition is always evaluated (sequenced) first, then one (and only one) of the branches is evaluated. In your example 1 is evaluated first, then a++ is evaluated and its result becomes the result of the entire expression. The (b ? c++ : d) part is not even touched.
1 ? a++ : b ? c++ : d;
is equivalent to
if (1) {
a++;
}
else {
if (b) {
c++;
}
else {
d;
}
}
So, the output will be
2 1 1 1

cannot understand nested ternary operator

int main(void)
{
int i = 10, j =20;
j = i, j ? (i, j) ? i : j : j;
printf("%d %d", i, j);
return 0;
}
What is the output?
Please somebody guide me how to interpret the nested ternary operator in this case.
C is defined by a language grammar; a precedence table is a handy condensing of the grammar into something that humans can take in at a glance, but it doesn't exactly correspond to what the grammar specifies.
You may need to consult the language grammar in order to resolve associativity around a ternary operator. Personally I always explicitly use parentheses so that a reader who's not a language lawyer can still understand what's going on (and so that I don't make mistakes).
An example is:
c ? c = a : c = b
which must be parsed as
(c ? c = a : c) = b
which is illegal in C, since the ternary operator does not give an lvalue. Incidentally, the C++ grammar is different; in that language this is parsed as
c ? c = a : (c = b)
which is legal; and also the ternary operator can give an lvalue in C++.
In your case, the question is which of the following it is:
Z = ((i , j) ? X : Y)
Z = (i , (j ? X : Y))
(Z = i, j) ? X : Y
(Z = i), (j ? X : Y)
I believe the latter is correct here, so you should end up with j = i plus an expression with no side-effect.
What you have here is hopefully code you have read and you want to get rid of, not code you actually use.
You have a combination of two unparenthesed ternay operators and two uses of the comma operator.
Despite no use of parentheses, the nesting of ?: is unambiguous:
a ? b ? c : d : e
can only mean
a ? ( b ? c : d) : e
as there is no other way to interpret it.
The 2nd comma operator is in parentheses, so it is unambiguous as well.
The only point of doubt is the comma operator at the start, where you might want to consult a precedence table.
Here we see that , has the lowest precedence and thus we have
(j = i), (j ? (i, j) ? i : j : j);
which is a comma operator with an assignment as first expression and an unevaluated other expression as second expression, which is the result.
In short, if we omit the right side, which isn't used nevertheless, we just have j = i, but this expression lacks unreadability.
So the output is 10 10.
Great trap, this expression... but as it is written, the answer doesn't cover this. If I erroneously evaluated it as j = j ? i : j; I would get 10 10 as well.

Assignment statement used in conditional operators

Can anybody please tell me why this statement is giving an error - Lvalue Required
(a>b?g=a:g=b);
but this one is correct
(a>b?g=a:(g=b));
where a , b and g are integer variables , and a and b are taken as input from keyboard.
In the expression,
(a > b ? g = a : g = b);
the relational operator > has the highest precedence, so a > b is grouped as an operand. The conditional-expression operator ? : has the next-highest precedence. Its first operand is a>b, and its second operand is g = a. However, the last operand of the conditional-expression operator is considered to be g rather than g = b, since this occurrence of g binds more closely to the conditional-expression operator than it does to the assignment operator. A syntax error occurs because = b does not have a left-hand operand (l-value).
You should use parentheses to prevent errors of this kind and produce more readable code which has been done in your second statement
(a > b ? g = a : (g = b));
in which last operand g = b of : ? has an l-value g and thats why it is correct.
Alternatively you can do
g = a > b ? a : b
The expression:
(a>b?g=a:g=b)
parsed as:
(a>b?g=a:g)=b
And we can't assign to an expression so its l-value error.
Read: Conditional operator differences between C and C++ Charles Bailey's answer:
Grammar for ?: is as follows:
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
This means that a ? b : c = d parses as (a ? b : c) = d even though (due to the 'not an l-value' rule) this can't result in a valid expression.
One side note:
Please keep space in you expression so that it become readable for example.
(a>b?g=a:g=b);
Should be written as:
(a > b? g = a: g = b);
similarly, you should add space after ; and ,.
The problem is operator precedence: In C the ternary conditional operator (?:) has a higher precedence than the assignment operator (=).
Without parenthesis (which don't do anything here) your expression would be this:
a > b ? g = a : g = b;
The operator with the highest precedence in there would be the comparison >, so this is where you'll get your first logical grouping:
(a > b) ? g = a : g = b;
The next highest expression is the ternary conditional, which results in the following expression:
((a > b) ? (g = a) : (g)) = b;
As you can see, you'll now end up with an lvalue (i.e. a value; not a variable) on the left side of your assignment operator, something that won't work.
As you already noticed, the solution to this is to simply group the expressions on your own. I'd even consider this good practice, especially if you're unsure how your precedence might play out. If you don't want to think about it, add parenthesis. Just keep code readability in mind, so if you can, resolve the operator precedence on your own, to ensure you've got everything right and readable.
As for readability: I'd probably use a classic if() here or move the assignment operator outside the ternary conditional, which is how you usually define max():
g = a > b ? a : b;
Or more general as a macro or inline function:
#define max(a, b) ((a) > (b) ? (a) : (b))
inline int max(int a, int b) {
return a > b ? a : b;
}
if(a>b)
{
g = a;
}
else
{
g = b;
}
that can be replaced with this
g = a > b ? a : b; //if a>b use the first (a) else use the second (b)
Your expression (a>b?g=a:g=b) is parsed as :
(a>b?g=a:g)=b
// ^^^
From the Microsoft documentation :
conditional-expression:
logical-or-expression
logical-or-expression ? expression : conditional-expression
In C, the operator ?: has an higher precedence that the operator =. Then it means that ( a ? b : c = d ) will be parsed as ( a ? b : c ) = d. Due to l-value's rule, the first expression is also valid but is not doing what you think.
To avoid this error, you can do also :
g = ( a > b ) ? a : b;
This question usually triggers a barrage of answers trying to explain the situation through the concept of operator precedence. In reality it cannot be explained that way, since this is a typical example of an input, on which surrogate concepts like "operator precedence" break down. As you probably know, there's really no "operator precedence" in C. There are only grammatical groupings, which generally cannot be expressed precisely through any linear ordering of operators.
Let's take a look at what the language specification says about it. The relevant portions of C grammar in this case are the grammars of ?: operator and = operator. For ?: operator it is
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
and for the = operator it is
assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
In the first case the critical part is the last operand of ?: operator: it is not an expression, but rather a conditional-expression. The conditional-expression is a different entry point into the grammar of C expression: it "enters" the grammar at the point where it is no longer possible to include a top-level = operator into a conditional-expression. The only way to "smuggle" a = operator into a conditional-expression is to descend the grammar all the way to the very bottom
primary-expression:
identifier
constant
string-literal
( expression )
generic-selection
and then wrap around all the way to the top using the ( expression ) branch. This means that a conditional-expression can contain a = operator only when it is explicitly wrapped in (...). E.g. the grammar prohibits you from having g = b as the last operand of ?: operator. If you want something like that, you have to explicitly parenthesize it: <smth> ? <smth> : (g = b).
A very similar situation exists with the second piece of grammar: assignment operator. The left-hand side (LHS) of assignment is unary-expression. And unary-expression "enters" the general grammar of C expression at the point where it is too late to include a top level ?: operator. The only way to reach the ?: operator from unary-expression is to descend all the way down to primary-expression and take the ( expression ) branch. This means that grammar prohibits you from having a > b ? g = a : g as the LHS operand of = operator. If you want something like that, you have to explicitly parentesize it: (a > b ? g = a : g) = <smth>.
For this reason "popular" answers claiming that "operator precedence" makes the language to parse your expression as
(a > b ? g = a : g) = b
are actually completely incorrect. In reality, there's no derivation tree in formal C grammar that would make your input fit the syntax of C language. Your input is not parsable at all. It is not an expression. It is simply syntactically invalid. C language sees it as a syntactic gibberish.
Now, in practice you might see some implementations to respond with a "lvalue required as left operand of assignment" diagnostic message. Formally, this is a misleading diagnostic message. Since the above input does not satisfy the grammar of C language expression, there's no "assignment" in it, there's no "left operand" and there's no meaningful "lvalue" requirement.
Why do compilers issue this strange message? Most likely they do indeed parse this input as a valid C expression
(a > b ? g = a : g) = b
The result of ?: is never an lvalue in C, hence the error. However, this interpretation of your input is non-standard (extended?) behavior, which has no basis in formal C language. This behavior of specific implementations might be caused by their attempts to reconcile C and C++ grammars (which are quite different in this area), by their attempts to produce a more readable (albeit "fake") error message or by some other reason.
Typically, in such implementations a similar issue also would pop up in case of inputs like
a + b = 5
The same error would be issued, suggesting a (a + b) = 5 parse, while from the pedantic point of view a + b = 5 is not parsable as an expression at all (for the same reasons as described above).
Again, formally, this is not enough to say that the compiler is "broken": the compiler is required to detect a constraint violation and issue some diagnostic message, which is exactly what happens here. The fact that the text of the diagnostic message does not correctly reflect the nature of the problem is inconsequential (the compiler can simply say "Ha ha ha!"). But one undesirable consequence of such misleading diagnostics is that it misleads users into misinterpreting the problem, which is BTW painfully evident from the barrage of formally incorrect answers posted to this question.

Unexpected error with conditional operator

The code below compiles well
int a=5,b=4,c;
a>b?30:40;
Also does,
int a=5,b=4,c;
a>b?c=30:40;
But why this does not work?
int a=5,b=4,c;
a>b?c=30:c=40;
You are being bitten by precedence. ?: has very low precedence, but not as low as = or , (see the operator precedence table).
Your code is parsed as:
(a>b ? c=30 : c) = 40;
Rather than:
a>b ? c=30 : (c=40);
You don't need parenthesis around c=30 because ? and : act like parentheses to the expression within.
Believe it or not, (a>b ? c=30 : c) = 40 is valid C++ (but not valid C). The expression (a>b ? c=30 : c) is an lvalue referencing the variable c, to which 40 is assigned.
You've run into a precedence problem with the = operator. If you insist on assignment inside of your ternary operator, merely wrap the sub expressions in parentheticals:
int d = a > b ? (c = 30) : (c = 40); // explicit precedence
The last one:
int a=5,b=4,c;
a>b?c=30:c=40;
fails because it's trying to assign 40 to a>b?c=30:c, which obviously won't work. The = has lower precedence, and a>b?c=30:c is a valid expression (though you can't assign to it). The = in the c=30 part is sort of an exception because it's in the middle of the ternary operator, between the ? and the :. To fix it you'd simply need to add parentheses around the c=40 so that it's evaluated as a single value for the 'else' part of the ternary operator, i.e. a>b?c=30:(c=40);
The second example
a>b?c=30:40;
doesn't assign anything to c unless a is greater than b... which it is when a is 5 and b is 4, as in this case; but note that if a were not greater than b, no assignment would occur.
From the first example
a>b?30:40
is a valid expression, with a value of 30 or 40, but you're not doing anything with that value so of course it serves no purpose there.
Of course, you'd normally use something more like:
c = a>b ? 30 : 40;
where a>b ? 30 : 40 will evaluate to 30 or 40, which is then assigned to c. But I suspect you know that and simply want to know why the c=40 is not treated as a single value for the 'else' part of the ternary operator in the last example.

Resources