Coming Soon

This lesson is currently being developed

Operator precedence and associativity

Learn the order in which operators are evaluated.

Operators
Chapter
Beginner
Difficulty
40min
Estimated Time

What to Expect

Comprehensive explanations with practical examples

Interactive coding exercises to practice concepts

Knowledge quiz to test your understanding

Step-by-step guidance for beginners

Development Status

In Progress

Content is being carefully crafted to provide the best learning experience

Preview

Early Preview Content

This content is still being developed and may change before publication.

6.1 — Operator precedence and associativity

In this lesson, you'll learn how C++ determines the order in which operators are evaluated in complex expressions, understand the difference between precedence and associativity, and master the rules that govern expression evaluation.

What is operator precedence?

Operator precedence determines which operations are performed first when multiple operators appear in the same expression. Just like in mathematics, multiplication and division are performed before addition and subtraction.

Think of precedence as a hierarchy - operators with higher precedence are evaluated before operators with lower precedence.

Consider this mathematical expression: 2 + 3 * 4

Do we calculate (2 + 3) * 4 = 20 or 2 + (3 * 4) = 14?

In mathematics (and C++), multiplication has higher precedence than addition, so we get 2 + (3 * 4) = 14.

Operator precedence in action

Let's see how precedence affects C++ expressions:

Basic arithmetic precedence

#include <iostream>

int main()
{
    // Multiplication has higher precedence than addition
    int result1 = 2 + 3 * 4;        // Evaluated as: 2 + (3 * 4) = 14
    
    // Division has higher precedence than subtraction
    int result2 = 10 - 8 / 2;       // Evaluated as: 10 - (8 / 2) = 6
    
    // Multiple high-precedence operators
    int result3 = 2 + 3 * 4 / 2;    // Evaluated as: 2 + ((3 * 4) / 2) = 8
    
    std::cout << "2 + 3 * 4 = " << result1 << std::endl;
    std::cout << "10 - 8 / 2 = " << result2 << std::endl;
    std::cout << "2 + 3 * 4 / 2 = " << result3 << std::endl;
    
    return 0;
}

Output:

2 + 3 * 4 = 14
10 - 8 / 2 = 6
2 + 3 * 4 / 2 = 8

Precedence with different operator types

#include <iostream>

int main()
{
    int x = 5;
    int y = 3;
    
    // Comparison operators have lower precedence than arithmetic
    bool result1 = x + y > 7;       // Evaluated as: (x + y) > 7 = true
    bool result2 = x * y == 15;     // Evaluated as: (x * y) == 15 = true
    bool result3 = x > y + 1;       // Evaluated as: x > (y + 1) = true
    
    std::cout << "x + y > 7: " << result1 << std::endl;
    std::cout << "x * y == 15: " << result2 << std::endl;
    std::cout << "x > y + 1: " << result3 << std::endl;
    
    return 0;
}

Output:

x + y > 7: 1
x * y == 15: 1
x > y + 1: 1

What is associativity?

Associativity determines the order of evaluation when operators of the same precedence appear in sequence. It answers: "Do we evaluate from left to right or right to left?"

There are three types of associativity:

  • Left-to-right: Operations are performed from left to right
  • Right-to-left: Operations are performed from right to left
  • No associativity: Cannot be chained (compiler error)

Left-to-right associativity

Most operators associate left-to-right:

#include <iostream>

int main()
{
    // Addition and subtraction associate left-to-right
    int result1 = 10 - 3 + 2;       // Evaluated as: (10 - 3) + 2 = 9
    
    // Multiplication and division associate left-to-right
    int result2 = 12 / 3 * 2;       // Evaluated as: (12 / 3) * 2 = 8
    
    // Mixed operations with same precedence
    int result3 = 20 / 4 * 3 / 2;   // Evaluated as: (((20 / 4) * 3) / 2) = 7
    
    std::cout << "10 - 3 + 2 = " << result1 << std::endl;
    std::cout << "12 / 3 * 2 = " << result2 << std::endl;
    std::cout << "20 / 4 * 3 / 2 = " << result3 << std::endl;
    
    return 0;
}

Output:

10 - 3 + 2 = 9
12 / 3 * 2 = 8
20 / 4 * 3 / 2 = 7

Right-to-left associativity

Some operators associate right-to-left:

#include <iostream>

int main()
{
    int a, b, c;
    
    // Assignment operators associate right-to-left
    a = b = c = 10;                 // Evaluated as: a = (b = (c = 10))
    
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    std::cout << "c = " << c << std::endl;
    
    // Compound assignment with right-to-left associativity
    int x = 5;
    int y = 3;
    x += y += 2;                    // Evaluated as: x += (y += 2)
                                    // y becomes 5, then x becomes 10
    
    std::cout << "x = " << x << std::endl;
    std::cout << "y = " << y << std::endl;
    
    return 0;
}

Output:

a = 10
b = 10
c = 10
x = 10
y = 5

C++ operator precedence table

Here's a comprehensive precedence table (from highest to lowest precedence):

Precedence Operator Description Associativity
1 (highest) () [] -> . Function calls, subscript, member access Left-to-right
2 ++ -- + - ! ~ Unary operators Right-to-left
3 * / % Multiplicative Left-to-right
4 + - Additive Left-to-right
5 << >> Bitwise shift Left-to-right
6 < <= > >= Relational Left-to-right
7 == != Equality Left-to-right
8 & Bitwise AND Left-to-right
9 ^ Bitwise XOR Left-to-right
10 ` ` Bitwise OR
11 && Logical AND Left-to-right
12 ` `
13 ?: Ternary conditional Right-to-left
14 = += -= etc. Assignment Right-to-left
15 (lowest) , Comma Left-to-right

Complex precedence example

#include <iostream>

int main()
{
    int a = 2, b = 3, c = 4, d = 5;
    
    // Complex expression with multiple precedence levels
    bool result = a + b * c > d && c - a == b;
    
    // Let's break down the evaluation step by step:
    // 1. b * c = 3 * 4 = 12 (multiplication first)
    // 2. a + (b * c) = 2 + 12 = 14 (addition next)
    // 3. c - a = 4 - 2 = 2 (subtraction, same level as addition)
    // 4. (a + b * c) > d = 14 > 5 = true (relational comparison)
    // 5. (c - a) == b = 2 == 3 = false (equality comparison)
    // 6. true && false = false (logical AND last)
    
    std::cout << "Complex expression result: " << result << std::endl;
    
    // Verify our step-by-step evaluation
    int step1 = b * c;
    int step2 = a + step1;
    int step3 = c - a;
    bool step4 = step2 > d;
    bool step5 = step3 == b;
    bool step6 = step4 && step5;
    
    std::cout << "Step-by-step verification:" << std::endl;
    std::cout << "b * c = " << step1 << std::endl;
    std::cout << "a + (b * c) = " << step2 << std::endl;
    std::cout << "c - a = " << step3 << std::endl;
    std::cout << "(a + b * c) > d = " << step4 << std::endl;
    std::cout << "(c - a) == b = " << step5 << std::endl;
    std::cout << "Final result = " << step6 << std::endl;
    
    return 0;
}

Output:

Complex expression result: 0
Step-by-step verification:
b * c = 12
a + (b * c) = 14
c - a = 2
(a + b * c) > d = 1
(c - a) == b = 0
Final result = 0

Using parentheses to override precedence

Parentheses have the highest precedence and can override the default order of operations:

Parentheses for clarity and control

#include <iostream>

int main()
{
    // Without parentheses - default precedence
    int result1 = 2 + 3 * 4;        // = 14
    
    // With parentheses - forced order
    int result2 = (2 + 3) * 4;      // = 20
    
    // Multiple levels of parentheses
    int result3 = ((2 + 3) * 4) / (5 - 3);  // = 10
    
    std::cout << "2 + 3 * 4 = " << result1 << std::endl;
    std::cout << "(2 + 3) * 4 = " << result2 << std::endl;
    std::cout << "((2 + 3) * 4) / (5 - 3) = " << result3 << std::endl;
    
    // Parentheses for readability
    int x = 10, y = 5, z = 2;
    
    // Hard to read without parentheses
    bool complex1 = x > y && y > z || x == y + z;
    
    // Much clearer with parentheses
    bool complex2 = (x > y) && (y > z) || (x == (y + z));
    
    std::cout << "Complex expression 1: " << complex1 << std::endl;
    std::cout << "Complex expression 2: " << complex2 << std::endl;
    
    return 0;
}

Output:

2 + 3 * 4 = 14
(2 + 3) * 4 = 20
((2 + 3) * 4) / (5 - 3) = 10
Complex expression 1: 1
Complex expression 2: 1

Nested parentheses

#include <iostream>

int main()
{
    // Deeply nested expression
    int result = ((5 + 3) * (4 - 1)) / ((6 / 2) + 1);
    
    // Breaking it down:
    // (5 + 3) = 8
    // (4 - 1) = 3  
    // (6 / 2) = 3
    // 8 * 3 = 24
    // 3 + 1 = 4
    // 24 / 4 = 6
    
    std::cout << "Complex nested result: " << result << std::endl;
    
    // Step by step for verification
    int inner1 = 5 + 3;         // 8
    int inner2 = 4 - 1;         // 3
    int inner3 = 6 / 2;         // 3
    int numerator = inner1 * inner2;    // 24
    int denominator = inner3 + 1;       // 4
    int final = numerator / denominator; // 6
    
    std::cout << "Step by step: " << inner1 << " * " << inner2 
              << " / (" << inner3 << " + 1) = " << final << std::endl;
              
    return 0;
}

Output:

Complex nested result: 6
Step by step: 8 * 3 / (3 + 1) = 6

Common precedence mistakes and how to avoid them

Mistake 1: Mixing arithmetic and comparison operators

#include <iostream>

int main()
{
    int score = 85;
    int passing = 60;
    
    // ❌ Confusing expression - what does this mean?
    // bool isPassing = score > passing + 10; 
    // Is this: (score > passing) + 10 or score > (passing + 10)?
    
    // ✅ Clear with parentheses
    bool isExcellent = score > (passing + 10);  // score > 70
    bool isAboveAverage = (score > passing) && (score < 90);
    
    std::cout << "Score: " << score << std::endl;
    std::cout << "Excellent (>70): " << isExcellent << std::endl;
    std::cout << "Above average (60-90): " << isAboveAverage << std::endl;
    
    return 0;
}

Output:

Score: 85
Excellent (>70): 1
Above average (60-90): 1

Mistake 2: Assignment vs. equality in conditions

#include <iostream>

int main()
{
    int x = 5;
    
    // ❌ Common mistake - assignment instead of comparison
    // if (x = 10)  // This assigns 10 to x, then checks if 10 is true (it is)
    
    // ✅ Correct comparison
    if (x == 10) {
        std::cout << "x equals 10" << std::endl;
    } else {
        std::cout << "x does not equal 10 (x = " << x << ")" << std::endl;
    }
    
    // Demonstrating the assignment mistake
    int y = 5;
    if (y = 10) {  // Assignment! y becomes 10, condition is true
        std::cout << "This will always execute! y is now: " << y << std::endl;
    }
    
    return 0;
}

Output:

x does not equal 10 (x = 5)
This will always execute! y is now: 10

Mistake 3: Logical operator precedence

#include <iostream>

int main()
{
    bool a = true;
    bool b = false;
    bool c = true;
    
    // ❌ Unclear intention
    // bool result = a || b && c;  // What's the intention here?
    
    // This evaluates as: a || (b && c) because && has higher precedence than ||
    bool result1 = a || b && c;     // true || (false && true) = true
    
    // ✅ Be explicit about your intention
    bool result2 = (a || b) && c;   // (true || false) && true = true
    bool result3 = a || (b && c);   // true || (false && true) = true
    
    std::cout << "a || b && c = " << result1 << std::endl;
    std::cout << "(a || b) && c = " << result2 << std::endl;
    std::cout << "a || (b && c) = " << result3 << std::endl;
    
    return 0;
}

Output:

a || b && c = 1
(a || b) && c = 1
a || (b && c) = 1

Best practices for precedence and associativity

1. Use parentheses for clarity

// ❌ Hard to read
int result = a + b * c / d - e % f;

// ✅ Clear intent
int result = a + ((b * c) / d) - (e % f);

2. Break complex expressions into steps

#include <iostream>

int main()
{
    int a = 10, b = 5, c = 3, d = 2;
    
    // ❌ Complex one-liner
    int complex = (a + b) * c / d + a % c - b / d;
    
    // ✅ Broken into clear steps
    int sum = a + b;            // 15
    int product = sum * c;      // 45
    int division1 = product / d; // 22
    int remainder = a % c;      // 1
    int division2 = b / d;      // 2
    int result = division1 + remainder - division2; // 21
    
    std::cout << "Complex result: " << complex << std::endl;
    std::cout << "Step-by-step result: " << result << std::endl;
    
    return 0;
}

Output:

Complex result: 21
Step-by-step result: 21

3. Remember the most important precedence rules

  • Parentheses always come first
  • Multiplication and division before addition and subtraction
  • Arithmetic before comparisons
  • Comparisons before logical operations
  • Assignment comes last

4. When in doubt, add parentheses

// If you're not sure about precedence, parentheses never hurt
int result = ((a + b) * c) / ((d - e) + f);

Summary

Operator precedence and associativity control how C++ evaluates complex expressions:

Precedence determines which operators are evaluated first:

  • Higher precedence operators are evaluated before lower precedence operators
  • Multiplication/division before addition/subtraction
  • Arithmetic before comparisons, comparisons before logical operations

Associativity determines evaluation order for same-precedence operators:

  • Most operators associate left-to-right
  • Assignment and ternary operators associate right-to-left

Key practices:

  • Use parentheses to make your intentions clear
  • Break complex expressions into simpler steps
  • Learn the most common precedence relationships
  • When in doubt, use parentheses rather than relying on precedence rules

Understanding precedence and associativity helps you write correct expressions and debug problems where operations aren't happening in the order you expected.

Quiz

  1. What is the result of 2 + 3 * 4 - 1? a) 19 b) 13 c) 18 d) 17

  2. How does x = y = z = 5 get evaluated? a) (x = y) = (z = 5) b) x = (y = (z = 5)) c) Left to right d) Undefined

  3. What is the result of 10 - 6 / 2 + 1? a) 3 b) 8 c) 2 d) 9

  4. Which operators have higher precedence than addition (+)? a) Multiplication and division b) Comparison operators c) Logical AND d) Assignment

  5. What associativity do most arithmetic operators have? a) Right-to-left b) Left-to-right c) No associativity d) Context-dependent

Practice exercises

Try these exercises to master precedence and associativity:

  1. Expression Evaluator: Write a program that evaluates complex expressions step-by-step, showing how precedence affects the result.

  2. Precedence Comparison: Create expressions that produce different results depending on where you place parentheses.

  3. Bug Hunter: Find and fix precedence-related bugs in expressions that don't evaluate as intended.

  4. Calculator Logic: Build a simple expression parser that respects C++ precedence rules.

Continue Learning

Explore other available lessons while this one is being prepared.

View Course

Explore More Courses

Discover other available courses while this lesson is being prepared.

Browse Courses

Lesson Discussion

Share your thoughts and questions

💬

No comments yet. Be the first to share your thoughts!

Sign in to join the discussion