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 assumep
is2
)2 == 2
results in true henceoperand2
doesn't get evaluated andif
blocks looks like asif(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.
For avoidance of doubt,
++x
is the new value ofx
, andz++
is the old value ofz
.Note also that because
||
is a sequencing point, the expression would be well defined even had you writtenx++
on the right hand side, rather thanz++
.Calling the result of this
sum
is an exercise in obfuscation.
Related Topics
Why Can't Std::Ostream Be Moved
How to Include the String Header
C++ [Windows] Path to the Folder Where the Executable Is Located
Std::Unique_Ptr with Derived Class
Is C++ Static Member Variable Initialization Thread-Safe
Do Pthread Mutexes Work Across Threads If in Shared Memory
How to Access Variables Defined and Declared in One Function in Another Function
Extracting 3D Coordinates Given 2D Image Points, Depth Map and Camera Calibration Matrices
Why Was Std::Strstream Deprecated
Shared_From_This Causing Bad_Weak_Ptr
How to Do an If Else Depending Type of Type in C++ Template
What Is Assignment via Curly Braces Called? and Can It Be Controlled
How to Brace-Initialize an Std::Array of Std::Pairs
Int *Array = New Int[N]; What Is This Function Actually Doing
What Are Use Cases for Structured Bindings
How to Compare Two Vectors for Equality Element by Element in C++