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
In this answer7 clauses
  1. 1C++ operator precedence table
  2. 2What is associativity in C++?
  3. 3Why does a & b == c give the wrong answer?
  4. 4Why does cout << a ? b : c print the wrong thing?
  5. 5What does *p++ do?
  6. 6Is precedence the same as order of evaluation?
  7. 7When should you add parentheses?

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.

Three hatched blocks in a row, joined by two lines with a ring on each. The second and third, set closer, sit in a pale blue band between two blue square brackets.
Figure 1 — Some operators take their operands before others.

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:

[Example 1:
#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:

[Example 2:
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.

Two trees. Left, 2 + 3 * 4: a plus node holds 2 and a times node holding 3 and 4. Right, 10 - 4 - 3: the top minus holds a lower minus (10 and 4) and 3.
Figure 2 — Precedence and associativity decide the shape of an expression: which operator gets which operands.

Division groups from the left too, and !, like every unary operator, sits above *, + and the comparisons:

[Example 3:
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:

[Example 4:
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:

[Example 5:
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::cout << ok ? "yes" : "no"; is read as (std::cout << ok) ? "yes" : "no";: it prints ok and drops the word. The << of a stream keeps the shift operator’s level, since overloading does not change precedence.

[Example 6:
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::cout << flags & mask; fails another way: it groups as (std::cout << flags) & mask, 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::basic_ostream<char>' and 'int').

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

[Example 7:
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:

[Example 8:
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 as f() + (g() * h()). All four ran f, g, h in that order and printed [f][g][h] = 7, but the standard fixes no order for these calls.
  • print(f(), g()), a function of two ints: the arguments of a call have no order, and the compilers split. Clang 23.1 on Linux ran f first; MSVC, GCC and the Windows Clang 19.1 ran g first.
  • std::cout << f() << ' ' << g() printed [f]1 [g]2 on all four, because since C++17 the left operand of << is computed first. Built as C++14, MSVC ran g first.

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

  1. C++ Operator Precedencecppreference.com
  2. Order of evaluationcppreference.com
  3. [expr.pre] Expressions: preambleC++ standard working draft
  4. [expr.shift] Shift operatorsC++ standard working draft
  5. [expr.assign] Assignment and compound assignment operatorsC++ standard working draft

Read next

  1. C++ operators: the full list with examplesLanguage · Miriam Castellanos · written for C++17
  2. C++ friend class and friend function: syntax and rulesLanguage · Miriam Castellanos · written for C++17
  3. C++ operator overloading: signatures, member or friendLanguage · Miriam Castellanos · written for C++20