Language[lang]
C++ operator precedence: table, rules and pitfalls
- Section
- Language [lang]
- Standard
- C++17
- Compilers
- MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
- Published
- Length
- 7 min read
Abstract
C++ operator precedence decides which operator takes its operands first: 2 + 3 * 4 is 2 + (3 * 4). Associativity settles operators of the same level: most group from the left, so 10 - 4 - 3 is (10 - 4) - 3, while prefix operators such as ! and unary -, ?: and assignment group from the right. Neither decides which operand is computed first: for the calls in f() + g() * h(), and for the arguments of a function call, the standard fixes no order.

C++ operator precedence decides which operator takes its operands first when an expression has several: 2 + 3 * 4 is 2 + (3 * 4), because * ranks above +. Associativity settles operators of the same level: 10 - 4 - 3 is (10 - 4) - 3, and a = b = c is a = (b = c). Neither decides which operand is computed first.
The smallest whole program, with each grouping in a comment:
#include <iostream>
int main() {
std::cout << 2 + 3 * 4 << '\n'; // 2 + (3 * 4)
std::cout << (2 + 3) * 4 << '\n';
std::cout << 10 - 4 - 3 << '\n'; // (10 - 4) - 3
std::cout << 10 - (4 - 3) << '\n';
int a = 0;
int b = 0;
a = b = 5; // a = (b = 5)
std::cout << a << ' ' << b << '\n';
}— end example]
It printed:
14
20
3
9
5 5— end example]
Parentheses override both rules: (2 + 3) * 4 gave 20 and 10 - (4 - 3) gave 9. Each << gets the whole calculation as its right operand, because << ranks below +, - and *. The samples on this page are C++17, built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++); where the four differed, the section says so. Since When lists which standard added each feature.
C++ operator precedence table
The table is the C++ order of operations: an operator in a higher row binds tighter, taking its operands before the operators below it. The level numbers are those of cppreference’s operator precedence table, without the rows and operators a beginner rarely meets, and “groups from” is the associativity.
| Level | Operators | Groups from |
|---|---|---|
| 2 | a++ a-- a() a[] . -> |
left to right |
| 3 | ++a --a +a -a ! ~ *a &a (type) sizeof |
right to left |
| 5 | * / % |
left to right |
| 6 | + - |
left to right |
| 7 | << >> |
left to right |
| 9 | < <= > >= |
left to right |
| 10 | == != |
left to right |
| 11 | & |
left to right |
| 12 | ^ |
left to right |
| 13 | | |
left to right |
| 14 | && |
left to right |
| 15 | || |
left to right |
| 16 | ?: = += -= and the other compound assignments |
right to left |
| 17 | , |
left to right |
Level 1 is ::, level 4 holds .* and ->*, and level 8 is <=> (C++20), between the shifts and <. The standard itself has no such table: precedence “is not directly specified, but it can be derived from the syntax” (expr.pre).
What is associativity in C++?
Associativity decides how operators of the same level group when they stand side by side. Most levels group from the left, so 10 - 4 - 3 printed 3; prefix operators such as ! and unary -, ?: and assignment group from the right, so a = b = 5 is a = (b = 5) and set both to 5.

Division groups from the left too, and !, like every unary operator, sits above *, + and the comparisons:
std::cout << std::boolalpha;
std::cout << 100 / 10 / 2 << '\n'; // (100 / 10) / 2
std::cout << 100 / (10 / 2) << '\n';
// ...
int count = 0;
std::cout << (!count == 2) << '\n'; // (!count) == 2
std::cout << !(count == 2) << '\n';— end example]
The divisions printed 5 and 20. !count == 2 printed false and !(count == 2) printed true: the first compares !count, a bool, with 2, and MSVC’s warning says why that is never true: “no value of type ‘bool’ promoted to type ‘int’ can equal the given constant”.
Why does a & b == c give the wrong answer?
Because == sits one level above &, flags & mask == mask is read as flags & (mask == mask), which is not a bit test. With flags 6 and mask 4, it took the wrong branch:
int flags = 0b110; // 6
int mask = 0b100; // 4
if (flags & mask == mask) { // flags & (mask == mask)
std::cout << "first: bit set\n";
} else {
std::cout << "first: bit not set\n";
}
if ((flags & mask) == mask) {
std::cout << "second: bit set\n";
} else {
std::cout << "second: bit not set\n";
}— end example]
It printed first: bit not set and second: bit set. All four compilers warned about the first if; Clang’s warning is the clearest:
warning: & has lower precedence than ==; == will be evaluated first [-Wparentheses]— end example]
Its “evaluated first” means grouped first: precedence does not decide what is computed first. The bitwise &, ^ and | all sit below the comparisons, so put every bit test in parentheses.
Why does cout << a ? b : c print the wrong thing?
Because << sits far above ?:, std:: is read as (std::: it prints ok and drops the word. The << of a stream keeps the shift operator’s level, since overloading does not change precedence.
bool ok = true;
std::cout << ok ? "yes" : "no"; // (std::cout << ok) ? ...
std::cout << '\n';
std::cout << (ok ? "yes" : "no") << '\n';
int shifted = 1 << 2 + 3; // 1 << (2 + 3)
int grouped = (1 << 2) + 3;
std::cout << shifted << ' ' << grouped << '\n';— end example]
It printed 1, then yes, then 32 7. GCC and Clang warned about the first line, but MSVC at /W4 said nothing, so do not count on a warning: put a ternary operator inside a std::cout chain in parentheses. The shift line is the arithmetic case: + sits above <<, so 1 << 2 + 3 is 1 << (2 + 3), 32 rather than 7.
std:: fails another way: it groups as (std::, there is no & for a stream and an int, and none of the four compiled it. GCC said no match for 'operator&' (operand types are 'std::.
What does *p++ do?
*p++ reads the element p points at and moves p on to the next one: postfix ++ sits one level above the * that reads through a pointer, so it is *(p++). To add one to the element instead, write (*p)++.
int scores[] = {10, 20, 30};
int* p = scores;
int first = *p++; // *(p++): reads scores[0], p moves on
int second = *p++; // reads scores[1], p moves on
(*p)++; // adds one to scores[2], p stays— end example]
first and second got 10 and 20, p ended at scores[2], and (*p)++ turned scores[2] from 30 into 31. What & and * do with pointers has the rest.
Is precedence the same as order of evaluation?
No. Precedence and associativity decide which operands belong to which operator, when the code is compiled; the order of evaluation decides which operand is computed first, when it runs. Three expressions whose calls print their names:
int f() { std::cout << "[f]"; return 1; }
int g() { std::cout << "[g]"; return 2; }
int h() { std::cout << "[h]"; return 3; }
// ...
int total = f() + g() * h(); // f() + (g() * h())
std::cout << " = " << total << '\n';
print(f(), g()); // no order between arguments
std::cout << f() << ' ' << g() << '\n'; // C++17: left first— end example]
f() + g() * h()groups asf() + (g() * h()). All four ranf,g,hin that order and printed[f][g][h] = 7, but the standard fixes no order for these calls.print(f(), g()), a function of twoints: the arguments of a call have no order, and the compilers split. Clang 23.1 on Linux ranffirst; MSVC, GCC and the Windows Clang 19.1 rangfirst.std::printedcout << f() << ' ' << g() [f]1 [g]2on all four, because since C++17 the left operand of<<is computed first. Built as C++14, MSVC rangfirst.
C++17 fixed the order for a few operators: a << b and a >> b compute a first (expr.shift), as does a[b], and a = b and the compound assignments compute b first (expr.assign); all four compilers here followed them as C++17.
When should you add parentheses?
Whenever the reader would have to look up a level, and always in the cases that went wrong above:
| Expression mixes | Parentheses |
|---|---|
&, ^ or | with a comparison |
always: (flags & mask) == mask |
?:, &, | or a comparison after std::cout << |
always |
! with a comparison |
always: !(count == 2) |
&& with || |
yes, for the reader; GCC and Clang warn without them |
A shift with + or - |
yes: (1 << 2) + 3 |
* or / with + or - |
not needed |
C++ operators lists what each operator does, and the Language section has the other answers on the core language.
Questions
What is the order of operations in C++?
C++ groups postfix operators first, then prefix operators, then * / %, then + and -, the shifts, <, <=, > and >=, then == and !=, the bitwise &, ^ and |, then &&, ||, and last ?: and assignment, with the comma below them all. Parentheses override that order. It decides which operator gets which operands, not which operand is computed first.
What is the difference between precedence and associativity?
Precedence decides between operators on different levels: * ranks above +, so 2 + 3 * 4 is 2 + (3 * 4). Associativity decides between operators on the same level: - groups from the left, so 10 - 4 - 3 is (10 - 4) - 3, and = groups from the right, so a = b = c is a = (b = c).
Does operator precedence decide the order of evaluation?
No. Precedence groups operands with operators when the code is compiled; which operand is computed first is a separate rule. In f() + g() * h() the standard fixes no order for the three calls, and the arguments of a function call have none either. Since C++17, a << b and a[b] compute a first, and a = b computes b first.
Which has higher precedence, && or ||?
&& does: it sits one level above ||, so a || b && c is a || (b && c). That is grouping only: || always evaluates its left operand first and skips the right one when the left is true, so a(false) || b(true) && c(true) calls a, then b, then c, and a(true) || b(true) && c(true) calls a alone. GCC and Clang suggest parentheses around the && part.
Is the assignment operator right to left?
Yes. Assignment groups from the right, so a = b = c is a = (b = c): c is assigned to b, and the result of that assignment to a. With int a and b, a = b = 5 left both at 5. The compound assignments, such as +=, group the same way.
Do parentheses slow a C++ program down?
No. Precedence, associativity and parentheses are compile-time matters: they decide which operator gets which operands while the code is compiled. Parentheses that repeat the grouping the table already gives leave the expression as it was, so add them around any operator whose level a reader would have to look up.
Sources
- C++ Operator Precedencecppreference.com
- Order of evaluationcppreference.com
- [expr.pre] Expressions: preambleC++ standard working draft
- [expr.shift] Shift operatorsC++ standard working draft
- [expr.assign] Assignment and compound assignment operatorsC++ standard working draft