Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Increment and Decrement Operators
Use ++ and -- operators and understand why side effects can cause unexpected behavior.
Incrementing and decrementing variables
Adding 1 to a variable (incrementing) or subtracting 1 (decrementing) are such common operations that C++ provides dedicated operators for them:
| Operator | Symbol | Form | Operation |
|---|---|---|---|
| Prefix increment | ++ | ++var | Increment var, then return var |
| Prefix decrement | -- | --var | Decrement var, then return var |
| Postfix increment | ++ | var++ | Copy var, increment var, return the copy |
| Postfix decrement | -- | var-- | Copy var, decrement var, return the copy |
Each operator has two versions: prefix (operator before operand) and postfix (operator after operand).
Prefix increment and decrement
Prefix versions are straightforward: first modify the operand, then return the modified value:
#include <iostream>
int main()
{
int score{100};
int bonus{++score}; // score becomes 101, then 101 is assigned to bonus
std::cout << score << ' ' << bonus << '\n';
return 0;
}
Output:
101 101
Postfix increment and decrement
Postfix versions are more complex: first make a copy of the operand, then modify the original, finally return the copy:
#include <iostream>
int main()
{
int score{100};
int bonus{score++}; // copy score (100), increment score to 101, assign copy (100) to bonus
std::cout << score << ' ' << bonus << '\n';
return 0;
}
Output:
101 100
Let's trace line 6 in detail: A temporary copy of score is created with value 100. Then the actual score variable increments from 100 to 101. Finally, the copy (still 100) is returned and assigned to bonus. The temporary copy is then discarded.
Consequently, bonus ends up with 100 (the pre-increment value), while score has 101 (the post-increment value).
The postfix version requires more steps (creating a copy, modifying the original, returning the copy), making it potentially less efficient than the prefix version.
More examples
Here's a comparison showing the difference:
#include <iostream>
int main()
{
int lives{3};
int attempts{3};
std::cout << lives << ' ' << attempts << '\n';
std::cout << ++lives << ' ' << --attempts << '\n'; // prefix
std::cout << lives << ' ' << attempts << '\n';
std::cout << lives++ << ' ' << attempts-- << '\n'; // postfix
std::cout << lives << ' ' << attempts << '\n';
return 0;
}
Output:
3 3
4 2
4 2
4 2
5 1
On line 8, prefix operators modify before returning, so std::cout receives the updated values.
On line 10, postfix operators return copies of the original values before modification, so std::cout receives the pre-modification values. The changes only appear on line 11 when we print the variables again.
When to use prefix versus postfix
Often, both versions produce identical behavior:
int main()
{
int counter{0};
++counter; // counter becomes 1
counter++; // counter becomes 2
return 0;
}
When either version works, prefer prefix. Prefix operators are generally more efficient and less prone to causing confusion.
Favor prefix versions—they're more performant and less likely to cause surprises. Use postfix versions only when they make code significantly more concise or clearer than the equivalent prefix version.
Side effects
A function or expression has a side effect when it does something observable beyond producing a return value.
Common side effects include modifying object values, performing input/output, or updating a user interface (like enabling/disabling a button).
Most side effects are useful:
lives = 5; // assignment's side effect: permanently changes lives
++lives; // increment's side effect: permanently changes lives
std::cout << lives; // output's side effect: modifies console state
The assignment operator permanently changes lives to 5. The increment operator permanently increases lives by 1. The output operator modifies the console's state by displaying text.
Assignment operators, prefix increment/decrement, and postfix increment/decrement all have side effects that permanently change object values. Other operators (like arithmetic operators) return values without modifying their operands.
Side effects can cause order of evaluation issues
Side effects can lead to problems when evaluation order matters:
#include <iostream>
int calculateDamage(int baseDamage, int multiplier)
{
return baseDamage * multiplier;
}
int main()
{
int power{10};
int damage{calculateDamage(power, ++power)}; // undefined behavior!
// Is this calculateDamage(10, 11) or calculateDamage(11, 11)?
std::cout << damage << '\n'; // could be 110 or 121
return 0;
}
C++ doesn't define the order in which function arguments are evaluated. If the left argument evaluates first, this becomes calculateDamage(10, 11), yielding 110. If the right evaluates first, it becomes calculateDamage(11, 11), yielding 121. The problem exists because one argument has a side effect.
Advanced note: The C++ standard intentionally leaves evaluation order unspecified so compilers can optimize for each architecture's natural ordering.
The sequencing of side effects
C++ often doesn't specify when side effects must be applied. This causes undefined behavior when an object with a side effect appears multiple times in the same statement.
For example, a + ++a is unspecified. When a is 2, Visual Studio and GCC might evaluate this as 3 + 3, while Clang might evaluate it as 2 + 3. This depends on when each compiler applies the increment side effect.
Even when the standard is clear about evaluation, this area has historically had many compiler bugs. You can avoid these problems entirely by ensuring any variable with a side effect appears no more than once per statement.
C++ doesn't define the order of evaluation for function arguments or operator operands. Don't use a variable that has a side effect applied more than once in a single statement—the result may be undefined. One exception: simple assignments like `a = a + b` (essentially equivalent to `a += b`).
Summary
- Increment operators:
++adds 1 to a variable; decrement operators:--subtracts 1 from a variable - Prefix versions (
++var,--var): Modify the variable first, then return the modified value - Postfix versions (
var++,var--): Make a copy, modify the original, then return the copy (less efficient) - Performance: Prefer prefix versions—they're more performant and less confusing; use postfix only when it makes code clearer
- Side effects: Functions or expressions that do something observable beyond producing a return value (e.g., modifying variables, I/O operations)
- Operators with side effects: Assignment (
=,+=, etc.), increment/decrement (++,--) - Order of evaluation: C++ doesn't specify the order in which function arguments or operands are evaluated
- Undefined behavior risk: Using a variable with a side effect multiple times in the same statement causes undefined behavior (e.g.,
calculateDamage(power, ++power)) - Safe practice: Ensure any variable with a side effect appears no more than once per statement
Side effects are powerful but dangerous when combined with C++'s unspecified evaluation order. The safest approach is to keep statements simple: apply side effects in separate statements, and never use a variable with a side effect more than once in the same expression. This prevents subtle bugs and makes your code more predictable across different compilers.
Increment and Decrement Operators - Quiz
Test your understanding of the lesson.
Practice Exercises
Pre and Post Increment
Understand the difference between prefix (++x) and postfix (x++) increment operators.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!