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
	    
Now let us have a look of how C++ will parse the third expression a = 3 of the conditional operator as (a = 3). I am only showing the derivation of the last expression, not the others.
                   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.

Advertisement

8 thoughts on “C Q&A #6: An interesting difference between the C and C++ conditional operator

  1. 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)

    1. 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 :) .

  2. There is a funny spin on this, that is not directly related:

    #include <stdio.h>
     
    int main(void)
    {
        int a = 5, b = 10, c;
    
        c = (a > 3) ? (3, b = 5, 10) : (4, a = 3);
        printf ("%d %d %d\n", a, b, c);
    
        return 0;
    }
    

    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.

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

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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s