int main (void) { int a = 5, b = 10, c; c = a > 3 ? b = 5 : a = 3; printf ("%d %d %d\n", a, b, c); //cout << a << " " << b << " " << c << endl; return 0; }
Have a look at the above code, what will be the outcome if the code was in C language and if in C++ Language (replace with printf with cout)?
Directly going to the solution. Compilation error for C, and works perfect (no warnings) with C++. I saw this problem is a C question and answer book which didn’t bother to explain, so here is the explanation.
For C language, the compilation error is at line number 5 with the conditional statement. What some might have expected incorrectly is: first the a > 3 will be evaluated and if it is true then b = 5 will be evaluated, and the result of evaluation then is assigned to c. Or, if a > 3 is false then a = 3 will be evaluated and then the result will be assigned to c. This is incorrect.
In brief the answer is that the expression
c = a > 3 ? b = 5 : a = 3;
will be evaluated as
c = ((a > 3) ? (b = 5) : a) = 3;
In the above expression, the conditional expression will be evaluated first, because the conditional expression has higher operator precedence than the assignment operator. Next the second assignment operator will be evaluated first, because the equality operator has right-to-left associativity. Here lies the problem. The evaluation of the conditional expression is not an l-value, according to C99 Section 6.5.15 Paragraph 4 and footnote 95. First the conditional expression gets evaluated resulting a non-l-value, and next the second assignment gets evaluated, thus on the right hand side of the assignment operator there is a non-l-value therefore result in a compilation error.
If you use gcc to compiler, then you will get the error
conditional.c:7:25: error: lvalue required as left operand of assignment
On the other hand, C++ won’t complain. This is because C++ has a different set of rules, which leads c = a > 3 ? b = 5 : a = 3 to be interpreted as c = (a > 3) ? (b = 5) : (a = 3);. Related section from the standard is Section 5.16 which tells about the conditional expression in C++.
————————-
More Information about the C interpretation
Above I have assumed that the expression in the code will be evaluated as
c = ((a > 3) ? (b = 5) : a) = 3;
Why? And why not as follows ?
c = (a > 3) ? (b = 5) : (a = 3);
To answer this in one line, in C language, there are no grammer rules which leads the interpretation of the expression c = a > 3 ? b = 5 : a = 3; as c = (a > 3) ? (b = 5) : (a = 3);. Why? For that we need to dig deep into the standards and have a look at the production rules.
————————-
C99 Section 6.5.15 Paragraph 1 conditional-expression: logical-OR-expression logical-OR-expression ? expression : conditional-expression
and
C99 Section 6.5.16 Paragraph 1 assignment-expression: conditional-expression unary-expression assignment-operator assignment-expression assignment-operator: =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
The third operand of the ? : operator is a conditional-expression. If you expand it using the defined production rules, you will never reach an expression a = b. On the other hand the second operand is an expression, if you expand the second operand, an expression, may lead you to an assignment operation.
I am quoting the production rules for C language below. Try to trace it.
C99 Section 6.5.14 Paragraph 1 logical-OR-expression: logical-AND-expression logical-OR-expression || logical-AND-expression C99 Section 6.5.13 Paragraph 1 logical-AND-expression: inclusive-OR-expression logical-AND-expression && inclusive-OR-expression C99 Section 6.5.12 Paragraph 1 inclusive-OR-expression: exclusive-OR-expression inclusive-OR-expression | exclusive-OR-expression C99 Section 6.5.11 Paragraph 1 exclusive-OR-expression: AND-expression exclusive-OR-expression ^ AND-expression C99 Section 6.5.10 Paragraph 1 AND-expression: equality-expression AND-expression & equality-expression C99 Section 6.5.9 Paragraph 1 equality-expression: relational-expression equality-expression == relational-expression equality-expression != relational-expression C99 Section 6.5.8 Paragraph 1 relational-expression: shift-expression relational-expression < shift-expression relational-expression > shift-expression relational-expression <= shift-expression relational-expression >= shift-expression C99 Section 6.5.7 Paragraph 1 shift-expression: additive-expression shift-expression << additive-expression shift-expression >> additive-expression C99 Section 6.5.6 Paragraph 1 additive-expression: multiplicative-expression additive-expression + multiplicative-expression additive-expression - multiplicative-expression C99 Section 6.5.5 Paragraph 1 multiplicative-expression: cast-expression multiplicative-expression * cast-expression multiplicative-expression / cast-expression multiplicative-expression % cast-expression C99 Section 6.5.4 Paragraph 1 cast-expression: unary-expression ( type-name ) cast-expression C99 Section 6.5.3 Paragraph 1 unary-expression: postfix-expression ++ unary-expression -- unary-expression unary-operator cast-expression sizeof unary-expression sizeof ( type-name ) unary-operator: &, *, +, -, ~, ! C99 Section 6.5.2 Paragraph 1 postfix-expression: primary-expression postfix-expression [ expression ] postfix-expression ( argument-expression-list_opt ) postfix-expression . identifier postfix-expression -> identifier postfix-expression ++ postfix-expression -- ( type-name ) { initializer-list } ( type-name ) { initializer-list , } argument-expression-list: assignment-expression argument-expression-list , assignment-expression C99 Section 6.5.1 Paragraph 1 primary-expression: identifier constant string-literal ( expression )
—————————-
More information about the C++ interpretation
In C++
c = a > 3 ? b = 5 : a = 3;
will be interpreted as below
c = (a > 3) ? (b = 5) : (a = 3);
This is because C++ production rules are different than the C production rules.
C++ Section 5.16 conditional-expression: logical-or-expression logical-or-expression ? expression : assignment-expression
Note that the production rules for the conditional-expression is different in C++ when compared to C language.
I am also listing the required production rules in C++ and then using the rules I will derive that we can parse the last expression as (a = b).
C++ Section 5.17 assignment-expression: conditional-expression logical-or-expression assignment-operator assignment-expression throw-expression assignment-operator: =, *=, /=, %=, +=, -=, >>=, <<=, &=, ^=, |= C++ Section 5.18 expression: assignment-expression expression , assignment-expression C++ Section 5.15 logical-or-expression: logical-and-expression logical-and-expression || logical-and-expression C++ Section 5.14 logical-and-expression: inclusive-or-expression logical-and-expression && inclusive-or-expression C++ Section 5.13 inclusive-or-expression: exclusive-or-expression inclusive-or-expression | exclusive-or-expression C++ Section 5.12 exclusive-or-expression: and-expression exclusive-or-expression ^ and-expression C++ Section 5.11 and-expression: equality-expression and-expression & equality-expression C++ Section 5.10 equality-expression: relational-expression equality-expression == relational-expression equality-expression != relational-expression C++ Section 5.9 relational-expression: shift-expression relational-expression < shift-expression relational-expression > shift-expression relational-expression <= shift-expression relational-expression >= shift-expression C++ Section 5.8 shift-expression additive-expression shift-expression << additive-expression shift-expression >> additive-expression C++ Section 5.7 additive-expression: multiplicative-expression additive-expression + multiplicative-expression additive-expression - multiplicative-expression C++ Section 5.6 multiplicative-expression: multiplicative-expression * pm-expression multiplicative-expression / pm-expression multiplicative-expression % pm-expression C++ Section 5.5 pm-expression: cast-expression pm-expression .* cast-expression pm-expression ->* cast-expression C++ Section 5.4 cast-expression: unary-expression ( type-id ) cast-expression C++ Section 5.3 unary-expression: postfix-expression ++ cast-expression -- cast-expression unary-operator cast-expression sizeof unary-expression sizeof ( type-id ) new-expression delete-expression unary-operator: one of * & + - ! ~ C++ Section 5.2 postfix-expression: primary-expression postfix-expression [ expression ] postfix-expression ( expression-list ) simple-type-specifier ( expression-list ) typename :: nested-name-specifier identifier ( expression-list ) typename :: nested-name-specifier template identifier ( expression-list ) postfix-expression . template id-expression postfix-expression -> template id-expression postfix-expression . pseudo-destructor-name postfix-expression -> pseudo-destructor-name postfix-expression ++ postfix-expression -- dynamic_cast < type-id > ( expression ) static_cast < type-id > ( expression ) reinterpret_cast <type-id> ( expression ) const_cast < type-id > ( expression ) typeid ( expression ) typeid ( expression ) expression-list: assigned-expression expression-list , assignment-expression pseudo-destructor-name: :: nested-name-specifier type_name :: ~ typename :: nested-name-specifier template type_name :: ~ typename :: nested-name-specifier ~ typename C++ Section 5.1 primary-expression: literal this ( expression ) id-expression id-expression: unqualified-id qualified-id unqualified-id: identifier operator-function-id conversion-function-id ~class-name template-id C++ Section 2.13 literal: integer-literal character-literal floating-literal string-literal string-literal boolean-literal
conditional-expression | V logical-or-expression ? expression : assignment-expression | | +---------------+ | V logical-or-expression assignment-operator assignment-expression | | | V V V inclusive-or-expression = conditional-expression | | V V exclusive-or-expression logical-or-expression | | V V and-expression inclusive-or-expression | | V V equality-expression exclusive-or-expression | | V V relational-expression and-expression | | V V shift-expression equality-expression | | V V additive-expression relational-expression | | V V multiplicative-expression shift-expression | | V V pm-expression additive-expression | | V V cast-expression multiplicative-expression | | V V unary-expression pm-expression | | V V postfix-expression cast-expression | | V V primary-expression unary-expression | | V V id-expression postfix-expression | | V V unqualified-id primary-expression | | V V identifier literal | | V V a integer-literal | V . . s o m e m o r e e x p a n s i o n s s e c 2.13 . . | V 3
Sorry for listing long production rules quoted from the standards, I have tried to keep the required rules at hand so that you can trace them.
Nice article!
Since I use both C and C++, I often tends to put parenthesis to clarify and to be able to use code both in C++ and C context.
(You need to correct the first formula in “More about C++…”. I think you mean the initial one)
Hey!
Same here, I always use parenthesis whenever there is a confusion, or even when no confusion for clarity and easier reading.
Thanks for pointing out the error. One of the drawbacks of copy paste :) .
There is a funny spin on this, that is not directly related:
Some time ago some compilers had some difficulties with what to return after a parenthesis, and a construction with “,” was needed to make it clear.
Code looks interesting. Can you elaborate what you want to point out in this code? What I understand is that the left operands of the comma operator won’t be the result of the evaluation. Are you saying that previously some compiler had problems parsing the comma operator expression without the parenthesis?
And I have merged your comment into one.
Early 90:s I worked with more or less compliant cross compilers.
Those didn’t always return 5 if you had the expression (a=5). Especially if you made more complex operations inside brackets, like lets say (a = b+c*4). In this last case I needed to clarify with (a = b+c*4, a), to really be sure to use a.
Oh, that’s a pretty interesting information. Thanks for sharing it
Your explanation of the topic is really helpful. :)
Thanks for stopping by! I am glad to know you liked the post.