Who Defines C Operator Precedence and Associativity

Who defines C operator precedence and associativity?

Operator precedence is defined in the appropriate standard. The standards for C and C++ are the One True Definition of what exactly C and C++ are. So if you look closely, the details are there. In fact, the details are in the grammar of the language. For example, take a look at the grammar production rule for + and - in C++ (collectively, additive-expressions):

additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression

As you can see, a multiplicative-expression is a subrule of an additive-expression. This means that if you have something like x + y * z, the y * z expression is a subexpression of x + y * z. This defines the precedence between these two operators.

We can also see that the left operand of an additive-expression expands to another additive-expression, which means that with x + y + z, x + y is a subexpression of it. This defines the associativity.

Associativity determines how adjacent uses of the same operator will be grouped. For example, + is left-to-right associative, which means that x + y + z will be grouped like so: (x + y) + z.

Don't mistake this for order of evaluation. There is absolutely no reason why the value of z could not be computed before x + y is. What matters is that it is x + y that is computed and not y + z.

For the function call operator, left-to-right associativity means that f()() (which could happen if f returned a function pointer, for example) is grouped like so: (f())() (of course, the other direction wouldn't make any sense).

Now let's consider the example you were looking at:

f1() + f2() * f3()

The * operator has higher precedence than the + operator, so the expressions are grouped like so:

f1() + (f2() * f3())

We don't even have to consider associativity here, because we don't have any of the same operator adjacent to each other.

Evaluation of the functions call expressions is, however, completely unsequenced. There's no reason f3 couldn't be called first, then f1, and then f2. The only requirement in this case is that operands of an operator are evaluated before the operator is. So that would mean f2 and f3 have to be called before the * is evaluated and the * must be evaluated and f1 must be called before the + is evaluated.

Some operators do, however, impose a sequencing on the evaluation of their operands. For example, in x || y, x is always evaluated before y. This allows for short-circuiting, where y does not need to be evaluated if x is known already to be true.

The order of evaluation was previously defined in C and C++ with the use of sequence points, and both have changed terminology to define things in terms of a sequenced before relationship. For more information, see Undefined Behaviour and Sequence Points.

Associativity in C

Operator precedence/associativity specifies which operators that "glue together" with which operand. In this case they specify that the expression should be parsed as (a<=b) == (d>c) and not a<= (b==d) >c etc.

Since the relational operators have higher precedence than equality operators, associativity doesn't matter here. It would only matter in case you had written something like a <= b > c.

Furthermore, don't mix up operator precedence with order of evaluation. In this case either the a<=b expression or the d>c expression might be evaluated first, since the == operator doesn't specify an order of evaluation of its operands. It's unspecified behavior and might differ from case to case.

For details see What is the difference between operator precedence and order of evaluation?

Operator precedence and Associativity in C/C++

From the latest publicly available draft

5.6 Multiplicative operators [expr.mul]

1 The multiplicative operators *, /, and % group left-to-right.

multiplicative-expression:
pm-expression
multiplicative-expression * pm-expression
multiplicative-expression / pm-expression
multiplicative-expression % pm-expression

So parsing will go like:

int res = x / y * z / t;
int res = (x / y * z) / t;
int res = ((x / y) * z) / t;

Are C/C++ operator precedence & associativity rules ever violated?

For one example, the usual precedence table says that sizeof and cast expressions have the same precedence. Both the table and the standard say that they associate right-to-left.

This simplification is fine when you're looking at, say, *&foo, which means the same as *(&foo).

It might also suggest to you that sizeof (int) 1 is legal C++ and that it means the same thing as sizeof( (int) 1 ). But it's not legal, because in fact sizeof( type-id ) is a special thing of its own in the grammar. Its existence prevents sizeof (int) 1 from being a sizeof expression whose operand is a cast-expression whose operand is 1.

So I think you could say that the "sizeof ( type-id )" production in the C++ grammar is an exception to what the usual precedence/associativity tables say. They do accurately describe the "sizeof unary-expression" production.

Associativity of the same operator precedence -- *start++

The postfix ++ operator does have higher precedence than the dereference operator *. So the expression parses as:

total += *(start++);

What might be confusing you is that the result of the postfix ++ operator is the operand before it is incremented. The actual increment happens as an unsequenced side effect of the expression.

So this expression takes the original value of start, dereferences it, and adds that value to total. By the time the expression is fully evaluated, start is incremented.

Note that this differs from:

total += (*start)++;

Because this increments what start points to instead of start itself.

Precedence of C operators

Here

if ( p == 2 || p % 2 )

it looks like

if( operand1 || operand2)

where operand1 is p == 2 and operand2 is P % 2. Now the logical OR || truth table is

operand1  operand2  operand1 || operand2
0 0 0
0 1 1
1 0 1
1 1 1

From the above table, it's clear that if the first operand operand1 result is true then the result will always true & second operand operand2 doesn't get evaluated.

operand1 is ==>

  • p == 2 (lets assume p is 2)

  • 2 == 2 results in true hence operand2 doesn't get evaluated and if blocks looks like as

    if(true) { }

Lets assume p is 3 then operand1 i.e 2 == 3 is false i.e operand2 gets evaluated i.e 3%2 i.e 1 that means if blocks looks like

if(true)
{
}

Let's assume p is 4 then operand1 i.e 2 == 4 is false i.e operand2 gets evaluated i.e 4%2 i.e 0 that means if blocks looks like

if(false)
{
}

Hope the above explanation makes sense to you.

About the associativity and precedence, please look into manual page of operator

operator precedence in C?

Your expression is grouped as

(++x >= (y * 2)) || ((y % 2) && (z++ % 2))

and this is assigned to sum. This is specified by the grammar of C.

Note also that the right hand side of || is not evaluated if the left hand side is 1: which will mean that z is not incremented in that case.

  1. For avoidance of doubt, ++x is the new value of x, and z++ is the old value of z.

  2. Note also that because || is a sequencing point, the expression would be well defined even had you written x++ on the right hand side, rather than z++.

  3. Calling the result of this sum is an exercise in obfuscation.



Related Topics



Leave a reply



Submit