Coming Soon
This lesson is currently being developed
Increment/decrement operators and side effects
Learn prefix and postfix increment/decrement operators.
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
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.4 — Increment/decrement operators and side effects
In this lesson, you'll learn about the increment (++
) and decrement (--
) operators, understand the crucial difference between their prefix and postfix forms, and discover how side effects can create subtle bugs in your programs.
What are increment and decrement operators?
The increment operator (++
) increases a variable's value by 1, while the decrement operator (--
) decreases a variable's value by 1. These operators are shortcuts for the common operations of adding or subtracting 1.
Instead of writing x = x + 1
, you can simply write ++x
or x++
.
Instead of writing y = y - 1
, you can simply write --y
or y--
.
Both operators come in two forms:
- Prefix (
++x
,--x
): Operator comes before the variable - Postfix (
x++
,x--
): Operator comes after the variable
The form you choose affects when the increment/decrement happens and what value is returned.
Basic increment and decrement operations
Simple increment and decrement
#include <iostream>
int main()
{
int x = 5;
int y = 10;
std::cout << "Initial values: x = " << x << ", y = " << y << std::endl;
// Increment x by 1
++x; // x becomes 6
// Decrement y by 1
--y; // y becomes 9
std::cout << "After increment/decrement: x = " << x << ", y = " << y << std::endl;
// Multiple operations
++x; // x becomes 7
++x; // x becomes 8
--y; // y becomes 8
--y; // y becomes 7
std::cout << "After more operations: x = " << x << ", y = " << y << std::endl;
return 0;
}
Output:
Initial values: x = 5, y = 10
After increment/decrement: x = 6, y = 9
After more operations: x = 8, y = 7
Equivalency with arithmetic operators
#include <iostream>
int main()
{
int a = 5, b = 5, c = 5;
// These are all equivalent ways to add 1
a = a + 1; // Traditional form
b += 1; // Compound assignment
++c; // Prefix increment
std::cout << "All three should be 6:" << std::endl;
std::cout << "a = " << a << std::endl;
std::cout << "b = " << b << std::endl;
std::cout << "c = " << c << std::endl;
int x = 10, y = 10, z = 10;
// These are all equivalent ways to subtract 1
x = x - 1; // Traditional form
y -= 1; // Compound assignment
--z; // Prefix decrement
std::cout << "\nAll three should be 9:" << std::endl;
std::cout << "x = " << x << std::endl;
std::cout << "y = " << y << std::endl;
std::cout << "z = " << z << std::endl;
return 0;
}
Output:
All three should be 6:
a = 6
b = 6
c = 6
All three should be 9:
x = 9
y = 9
z = 9
Prefix vs. postfix: The crucial difference
The key difference between prefix and postfix forms is when the increment/decrement happens and what value is returned:
- Prefix (
++x
): Increment first, then return the new value - Postfix (
x++
): Return the current value first, then increment
Demonstrating the difference
#include <iostream>
int main()
{
// Prefix increment
int a = 5;
int prefixResult = ++a; // a is incremented to 6, then 6 is returned
std::cout << "Prefix increment:" << std::endl;
std::cout << "a after ++a: " << a << std::endl; // 6
std::cout << "Value returned by ++a: " << prefixResult << std::endl; // 6
// Postfix increment
int b = 5;
int postfixResult = b++; // Current value (5) is returned, then b is incremented to 6
std::cout << "\nPostfix increment:" << std::endl;
std::cout << "b after b++: " << b << std::endl; // 6
std::cout << "Value returned by b++: " << postfixResult << std::endl; // 5
// Same behavior with decrement
int c = 10;
int prefixDec = --c; // c decremented to 9, then 9 returned
int d = 10;
int postfixDec = d--; // 10 returned, then d decremented to 9
std::cout << "\nDecrement comparison:" << std::endl;
std::cout << "c after --c: " << c << ", returned value: " << prefixDec << std::endl;
std::cout << "d after d--: " << d << ", returned value: " << postfixDec << std::endl;
return 0;
}
Output:
Prefix increment:
a after ++a: 6
Value returned by ++a: 6
Postfix increment:
b after b++: 6
Value returned by b++: 5
Decrement comparison:
c after --c: 9, returned value: 9
d after d--: 9, returned value: 10
Step-by-step comparison
#include <iostream>
int main()
{
std::cout << "Step-by-step prefix vs postfix comparison:" << std::endl;
// Prefix example
int x = 5;
std::cout << "\nPrefix (++x):" << std::endl;
std::cout << "x before: " << x << std::endl; // 5
std::cout << "++x returns: " << (++x) << std::endl; // 6 (x incremented first)
std::cout << "x after: " << x << std::endl; // 6
// Reset and show postfix
x = 5;
std::cout << "\nPostfix (x++):" << std::endl;
std::cout << "x before: " << x << std::endl; // 5
std::cout << "x++ returns: " << (x++) << std::endl; // 5 (old value returned)
std::cout << "x after: " << x << std::endl; // 6 (but x was incremented)
return 0;
}
Output:
Step-by-step prefix vs postfix comparison:
Prefix (++x):
x before: 5
++x returns: 6
x after: 6
Postfix (x++):
x before: 5
x++ returns: 5
x after: 6
When the difference matters
In simple statements where you ignore the return value, prefix and postfix behave identically:
When prefix and postfix are equivalent
#include <iostream>
int main()
{
int a = 5, b = 5;
// When used as standalone statements, both do the same thing
++a; // a becomes 6
b++; // b becomes 6
std::cout << "Both should be 6: a = " << a << ", b = " << b << std::endl;
// In simple for loops, both work identically
std::cout << "Prefix version: ";
for (int i = 0; i < 5; ++i) {
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "Postfix version: ";
for (int i = 0; i < 5; i++) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
Output:
Both should be 6: a = 6, b = 6
Prefix version: 0 1 2 3 4
Postfix version: 0 1 2 3 4
When prefix and postfix behave differently
#include <iostream>
int main()
{
// Example 1: Assignment with increment
int x = 5;
int y = 5;
int prefixAssign = ++x; // x incremented to 6, then 6 assigned
int postfixAssign = y++; // 5 assigned, then y incremented to 6
std::cout << "Assignment with increment:" << std::endl;
std::cout << "prefixAssign = " << prefixAssign << ", x = " << x << std::endl; // 6, 6
std::cout << "postfixAssign = " << postfixAssign << ", y = " << y << std::endl; // 5, 6
// Example 2: Array indexing
int array[] = {10, 20, 30, 40, 50};
int index = 0;
std::cout << "\nArray access with increment:" << std::endl;
std::cout << "array[index++] = " << array[index++] << std::endl; // Uses index 0, then increments
std::cout << "Index is now: " << index << std::endl; // 1
std::cout << "array[++index] = " << array[++index] << std::endl; // Increments to 2, then uses index 2
std::cout << "Index is now: " << index << std::endl; // 2
// Example 3: Function arguments
auto printValue = [](int value) {
std::cout << "Function received: " << value << std::endl;
};
int counter = 10;
std::cout << "\nFunction calls with increment:" << std::endl;
printValue(++counter); // Increments to 11, passes 11
std::cout << "counter is now: " << counter << std::endl; // 11
printValue(counter++); // Passes 11, then increments to 12
std::cout << "counter is now: " << counter << std::endl; // 12
return 0;
}
Output:
Assignment with increment:
prefixAssign = 6, x = 6
postfixAssign = 5, y = 6
Array access with increment:
array[index++] = 10
Index is now: 1
array[++index] = 30
Index is now: 2
Function calls with increment:
Function received: 11
counter is now: 11
Function received: 11
counter is now: 12
Side effects and sequence points
A side effect is any change to program state that persists after an expression is evaluated. Increment and decrement operations have side effects because they modify variables.
Sequence points are points in program execution where all side effects of previous operations are guaranteed to be complete. In C++, sequence points occur at:
- The end of a full expression (semicolon)
- Before function calls
- At certain operators (
&&
,||
,,
,?:
)
Undefined behavior with multiple side effects
#include <iostream>
int main()
{
int x = 5;
// ❌ UNDEFINED BEHAVIOR - multiple modifications between sequence points
// The result of this is unpredictable and compiler-dependent
// int result = x++ + ++x; // DON'T DO THIS!
// ✅ Safe approach - separate the operations
int safe_x = 5;
int first = safe_x++; // safe_x becomes 6, first gets 5
int second = ++safe_x; // safe_x becomes 7, second gets 7
int safe_result = first + second; // 5 + 7 = 12
std::cout << "Safe calculation: " << first << " + " << second << " = " << safe_result << std::endl;
// More examples of undefined behavior to AVOID:
// int y = 1;
// int bad1 = y++ + y++; // Undefined behavior
// int bad2 = ++y + y; // Undefined behavior
// int bad3 = y++ * ++y; // Undefined behavior
// ✅ Safe versions:
int y = 1;
int temp1 = y++; // y becomes 2, temp1 = 1
int temp2 = y++; // y becomes 3, temp2 = 2
int good1 = temp1 + temp2; // 1 + 2 = 3
std::cout << "Safe version: " << temp1 << " + " << temp2 << " = " << good1 << std::endl;
return 0;
}
Output:
Safe calculation: 5 + 7 = 12
Safe version: 1 + 2 = 3
Common side effect mistakes
#include <iostream>
int main()
{
// ❌ Mistake 1: Modifying array index while accessing
int arr[] = {1, 2, 3, 4, 5};
int i = 0;
// This is undefined behavior - don't do this!
// arr[i] = i++; // Which i is used for the index?
// ✅ Safe approach
arr[i] = i; // Use current value
i++; // Then increment
// ❌ Mistake 2: Multiple increments in function parameters
auto print2Values = [](int a, int b) {
std::cout << "Values: " << a << ", " << b << std::endl;
};
int counter = 1;
// print2Values(counter++, counter++); // Undefined behavior!
// ✅ Safe approach
int first = counter++; // counter becomes 2, first = 1
int second = counter++; // counter becomes 3, second = 2
print2Values(first, second);
// ❌ Mistake 3: Self-assignment with increment
i = 2;
// i = i++; // Undefined behavior - is i assigned before or after increment?
// ✅ Just use increment by itself if that's what you want
++i; // or i++; Clear intention
std::cout << "Final i value: " << i << std::endl;
return 0;
}
Output:
Values: 1, 2
Final i value: 3
Practical applications
Loop counters
#include <iostream>
int main()
{
std::cout << "Forward counting with increment:" << std::endl;
for (int i = 1; i <= 5; ++i) {
std::cout << "Count: " << i << std::endl;
}
std::cout << "\nBackward counting with decrement:" << std::endl;
for (int i = 5; i >= 1; --i) {
std::cout << "Countdown: " << i << std::endl;
}
// While loop with increment
std::cout << "\nWhile loop example:" << std::endl;
int counter = 0;
while (counter < 3) {
std::cout << "Counter: " << counter << std::endl;
++counter; // Increment at end of loop
}
return 0;
}
Output:
Forward counting with increment:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Backward counting with decrement:
Countdown: 5
Countdown: 4
Countdown: 3
Countdown: 2
Countdown: 1
While loop example:
Counter: 0
Counter: 1
Counter: 2
Array traversal
#include <iostream>
int main()
{
int numbers[] = {10, 20, 30, 40, 50};
int size = 5;
// Forward traversal with postfix increment
std::cout << "Forward traversal:" << std::endl;
for (int i = 0; i < size; i++) { // i++ commonly used in for loops
std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl;
}
// Backward traversal with prefix decrement
std::cout << "\nBackward traversal:" << std::endl;
for (int i = size - 1; i >= 0; --i) { // --i slightly more efficient
std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl;
}
// Processing with increment
std::cout << "\nDoubling all values:" << std::endl;
for (int i = 0; i < size; ++i) {
numbers[i] *= 2;
std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl;
}
return 0;
}
Output:
Forward traversal:
numbers[0] = 10
numbers[1] = 20
numbers[2] = 30
numbers[3] = 40
numbers[4] = 50
Backward traversal:
numbers[4] = 50
numbers[3] = 40
numbers[2] = 30
numbers[1] = 20
numbers[0] = 10
Doubling all values:
numbers[0] = 20
numbers[1] = 40
numbers[2] = 60
numbers[3] = 80
numbers[4] = 100
Practical counting and processing
#include <iostream>
int main()
{
// Count positive and negative numbers
int numbers[] = {5, -3, 8, -1, 0, 4, -7, 2};
int size = 8;
int positiveCount = 0;
int negativeCount = 0;
int zeroCount = 0;
for (int i = 0; i < size; ++i) {
if (numbers[i] > 0) {
++positiveCount;
} else if (numbers[i] < 0) {
++negativeCount;
} else {
++zeroCount;
}
}
std::cout << "Number analysis:" << std::endl;
std::cout << "Positive numbers: " << positiveCount << std::endl;
std::cout << "Negative numbers: " << negativeCount << std::endl;
std::cout << "Zeros: " << zeroCount << std::endl;
// Process until condition met
std::cout << "\nProcessing until sum exceeds 50:" << std::endl;
int values[] = {5, 10, 15, 20, 25, 30};
int sum = 0;
int index = 0;
while (sum <= 50 && index < 6) {
sum += values[index];
std::cout << "Added " << values[index] << ", sum is now " << sum << std::endl;
++index;
}
return 0;
}
Output:
Number analysis:
Positive numbers: 4
Negative numbers: 3
Zeros: 1
Processing until sum exceeds 50:
Added 5, sum is now 5
Added 10, sum is now 15
Added 15, sum is now 30
Added 20, sum is now 50
Added 25, sum is now 75
Best practices
1. Prefer prefix when the return value isn't used
// ✅ In loops, prefer prefix (slightly more efficient for complex types)
for (int i = 0; i < 10; ++i) { // Preferred
// Loop body
}
// This works too, but postfix creates a temporary copy
for (int i = 0; i < 10; i++) { // Acceptable for built-in types
// Loop body
}
2. Use separate statements for clarity
// ❌ Confusing - what happens when?
int result = arr[index++] + arr[++index];
// ✅ Clear and safe
int first = arr[index];
++index;
int second = arr[index];
++index;
int result = first + second;
3. Be consistent in your usage
// ✅ Pick a style and stick with it
for (int i = 0; i < size; ++i) { // Always use prefix in loops
for (int j = 0; j < cols; ++j) { // Consistent
// Process element
}
}
4. Avoid multiple modifications in one statement
// ❌ Undefined behavior
// int x = i++ + ++i;
// ✅ Split into clear steps
int temp1 = i++;
int temp2 = ++i;
int x = temp1 + temp2;
Summary
Increment and decrement operators provide convenient shortcuts for common operations:
Key concepts:
- Increment (
++
): Adds 1 to a variable - Decrement (
--
): Subtracts 1 from a variable - Prefix (
++x
): Increment first, return new value - Postfix (
x++
): Return current value, then increment
Critical differences:
- When used alone: prefix and postfix behave identically
- When return value is used: prefix and postfix behave differently
- Prefix is often preferred in loops (more efficient for complex types)
Side effects and safety:
- Multiple modifications between sequence points cause undefined behavior
- Always separate complex operations into clear steps
- Avoid modifying the same variable multiple times in one expression
Best practices:
- Use prefix when the return value isn't needed
- Keep operations simple and clear
- Separate complex expressions into multiple statements
- Be consistent in your coding style
Understanding these operators and their subtleties helps you write more efficient code and avoid hard-to-debug problems caused by undefined behavior.
Quiz
-
What value is printed by this code?
int x = 5; cout << ++x;
a) 5 b) 6 c) Undefined d) Compiler error
-
What value is printed by this code?
int x = 5; cout << x++;
a) 5 b) 6 c) Undefined d) Compiler error
-
After this code executes, what is the value of x?
int x = 10; int y = ++x;
a) 10 b) 11 c) Undefined d) 0
-
Which of these expressions causes undefined behavior? a)
++x
b)x++
c)x++ + ++x
d)++x; y = x;
-
Which is generally preferred in for loops? a)
i++
always b)++i
when return value not used c) No difference d) Depends on the loop type
Practice exercises
Try these exercises to master increment/decrement operators:
-
Counter Program: Write a program that demonstrates the difference between prefix and postfix operators in various scenarios.
-
Array Processor: Create a program that uses increment/decrement operators to traverse and modify array elements safely.
-
Loop Variations: Write the same counting loop using prefix increment, postfix increment, and traditional assignment to see the differences.
-
Side Effect Debugger: Find and fix undefined behavior in expressions that incorrectly use multiple increment/decrement operators.
Explore More Courses
Discover other available courses while this lesson is being prepared.
Browse CoursesLesson Discussion
Share your thoughts and questions