C++ · Beginner
Operators in C++
In short: C++ operators combine values into expressions: arithmetic (+ - * / %), comparison (== != < <= > >=), logical (&& || !) and assignment (= += -= *= /= %=). Division of two ints drops the fraction, % gives the remainder, comparisons produce a bool, and && and || stop evaluating as soon as the result is known.
How expressions are built
An operator takes one or two values, its operands, and produces a new value. The arithmetic operators are +, -, *, / and %. The one rule that catches everyone is that the result type follows the operand types: when both operands are ints, / performs integer division and throws the fractional part away, so 7 / 2 is 3 and -7 / 2 is -3, truncating toward zero. As soon as either operand is a double the division is done in floating point, so 7 / 2.0 is 3.5. The % operator gives the remainder of an integer division and only works on integers; 75 % 12 is 3, and since C++11 the sign of the result follows the sign of the left operand, so -7 % 2 is -1.
Comparison operators ==, !=, <, <=, > and >= compare two values and produce a bool. The logical operators combine bools: && is true only when both sides are true, || when at least one is, and ! flips a value. Both && and || short-circuit: they evaluate the left operand first and skip the right one when it can no longer change the answer. That is not just an optimisation. count != 0 && total / count > 5 is safe precisely because the division never runs when count is 0.
Assignment = stores the value on the right into the variable on the left. It is an expression too, which is why if (x = 5) compiles: it assigns 5 and then tests 5, which is true. The compound forms +=, -=, *=, /= and %= update a variable in place; balance += 25.5 means balance = balance + 25.5. The increment and decrement operators ++ and -- add or subtract 1. Prefix ++counter changes the variable and produces the new value; postfix counter++ produces the old value and then changes the variable. On its own line the two are interchangeable, and ++i is the conventional choice.
When operators are mixed, precedence decides which applies first: *, / and % bind tighter than + and -, arithmetic binds tighter than comparisons, comparisons tighter than &&, and && tighter than ||. Assignment binds loosest of all. Parentheses override everything and cost nothing, so use them whenever a reader might have to think: (2 + 3) * 4 says what it means, 2 + 3 * 4 relies on the reader knowing the rules.
One more family exists for later: the bitwise operators &, |, ^, ~, << and >> work on the individual bits of integers. You have already met << and >> as the stream operators; that is the same symbol given a second meaning for streams, which is why std::cout << 2 + 3 needs no parentheses but std::cout << (a < b) does.
Syntax
a + b a - b a * b a / b a % b // arithmetic; % needs integers
a == b a != b a < b a <= b a > b a >= b // comparison, result is bool
a && b a || b !a // logical; && and || short-circuit
x = v x += v x -= v x *= v x /= v x %= v // assignment forms
++x x++ --x x-- // add or subtract 1
condition ? valueIfTrue : valueIfFalse // the conditional operatorEach comparison yields exactly true or false. Printing a bool shows 1 or 0 unless std::cout << std::boolalpha; has been set earlier.
Precedence, highest first
| Operators | Meaning | Example |
|---|---|---|
| ( ) | grouping | (2 + 3) * 4 is 20 |
| x++ x-- | postfix increment and decrement | n++ yields the old n |
| ++x --x ! - (unary) | prefix increment, not, negation | !done, -total |
| * / % | multiply, divide, remainder | 2 + 3 * 4 is 14 |
| + - | add, subtract | 10 - 4 - 3 is 3 (left to right) |
| < <= > >= | ordering comparisons | 1 + 2 < 4 is true |
| == != | equality comparisons | a == b && c is (a == b) && c |
| && | logical and | evaluated before || |
| || | logical or | a || b && c is a || (b && c) |
| ?: | conditional | n > 0 ? n : -n |
| = += -= *= /= %= | assignment | x = y = 0 assigns right to left |
Integer division, remainder and precedence
Packing 75 eggs into boxes of 12 shows what / and % each give you.
#include <iostream>
int main() {
int eggs = 75;
int perBox = 12;
std::cout << "Full boxes: " << eggs / perBox << "\n";
std::cout << "Left over: " << eggs % perBox << "\n";
std::cout << "Boxes as a decimal: " << eggs / 12.0 << "\n";
std::cout << "7 / 2 = " << 7 / 2 << "\n";
std::cout << "-7 / 2 = " << -7 / 2 << "\n";
std::cout << "-7 % 2 = " << -7 % 2 << "\n";
std::cout << "2 + 3 * 4 = " << 2 + 3 * 4 << "\n";
std::cout << "(2 + 3) * 4 = " << (2 + 3) * 4 << "\n";
return 0;
}Output
Full boxes: 6 Left over: 3 Boxes as a decimal: 6.25 7 / 2 = 3 -7 / 2 = -3 -7 % 2 = -1 2 + 3 * 4 = 14 (2 + 3) * 4 = 20
eggs / perBox and eggs % perBox together split 75 into 6 full boxes and 3 loose eggs; the pair / and % is how you decompose a quantity into units and remainder. Changing the literal to 12.0 makes the division floating point. Negative division truncates toward zero, so -3.5 becomes -3, and the remainder keeps the sign of the dividend so that (a / b) * b + a % b always equals a. The last two lines show * binding before +.
Comparison, logic and short-circuiting
A bus fare rule combines age and distance; the final line divides by zero without crashing.
#include <iostream>
int main() {
int age;
int distanceKm;
std::cin >> age >> distanceKm;
bool isChild = age < 12;
bool isSenior = age >= 65;
bool longTrip = distanceKm > 20;
std::cout << std::boolalpha;
std::cout << "child: " << isChild << ", senior: " << isSenior << "\n";
std::cout << "discount: " << (isChild || isSenior) << "\n";
std::cout << "full fare on a long trip: " << (!isChild && !isSenior && longTrip) << "\n";
int passengers = 0;
// The right side never runs when the left side is false, so no division by zero.
std::cout << "busy route: " << (passengers != 0 && distanceKm / passengers > 5) << "\n";
return 0;
}Input given to the program: 70 35
Output
child: false, senior: true discount: true full fare on a long trip: false busy route: false
Each comparison is stored in a named bool, which makes the later logic read like the rule it implements. The parentheses around (isChild || isSenior) are required: << binds tighter than ||, so without them the compiler would try to send isChild to the stream and then || the stream with a bool. In the last line passengers != 0 is false, so && never evaluates distanceKm / passengers; integer division by zero would otherwise be undefined behaviour and typically crashes.
Increment, compound assignment and in-place update
Prefix and postfix ++ differ in what they hand back, not in what they do to the variable.
#include <iostream>
int main() {
int counter = 5;
int a = counter++; // a gets 5, then counter becomes 6
int b = ++counter; // counter becomes 7, then b gets 7
std::cout << "a = " << a << ", b = " << b << ", counter = " << counter << "\n";
double balance = 100.0;
balance += 25.5; // balance = balance + 25.5
balance *= 2; // balance = balance * 2
balance -= 1;
std::cout << "balance = " << balance << "\n";
int minutes = 135;
int hours = minutes / 60;
minutes %= 60; // keep only the remainder
std::cout << hours << "h " << minutes << "m\n";
return 0;
}Output
a = 5, b = 7, counter = 7 balance = 250 2h 15m
After both increments counter is 7 either way; the difference is that a received the value from before the postfix increment and b the value after the prefix one. The three compound assignments take balance from 100 to 125.5 to 251 to 250. minutes %= 60 is the idiom for wrapping a value into a range: it replaces 135 with 15 once the 2 full hours have been extracted by the division.
Common mistakes
Writing = when == was meant
Why it goes wrong:
if (level = 3)assigns 3 to level and tests the result, which is always true. The program compiles; g++ with -Wall only suggests parentheses around the assignment.Fix: Use
==to compare. Some people write the constant first,if (3 == level), so the mistake becomes a compile error.C++ · fixif (level == 3) { std::cout << "top floor\n"; }Dividing two ints when a fraction was wanted
Why it goes wrong:
double share = total / people;truncates before the assignment, so 7 divided among 2 gives 3, not 3.5.Fix: Convert one operand first:
static_cast<double>(total) / people.Chaining comparisons like maths
Why it goes wrong:
low < x < highcompiles, but it evaluateslow < xto true or false, converts that to 1 or 0, and compares that number withhigh. g++ -Wall warns that the expression does not have its mathematical meaning.Fix: Write the two comparisons joined with
&&.C++ · fixif (low < x && x < high) { // x is strictly between low and high }Testing doubles for exact equality
Why it goes wrong: Binary floating point cannot represent most decimal fractions exactly, so
0.1 + 0.2 == 0.3is false. Equality tests on computed doubles fail for reasons invisible in the source.Fix: Compare the difference against a small tolerance, for example
std::abs(a - b) < 1e-9, using<cmath>; or keep such values as integers in a smaller unit.
Where you use this
The % operator is the everyday tool for anything cyclic. A cleaning rota with 7 volunteers assigns day n to volunteer n % 7; when n reaches 7 the result wraps back to 0 without any if statement. The same idea converts a running total of seconds into hours, minutes and seconds, tests whether a number is even (n % 2 == 0), extracts the last digit of a number (n % 10), and steps through a circular buffer. Combined with /, which tells you how many whole cycles have passed, you can decompose any quantity into units and remainder in two lines.
int volunteers = 7;
int day = 12;
int onDuty = day % volunteers; // 5
int weeksCompleted = day / volunteers; // 1Key points
- int / int is integer division; make one operand a double to keep the fraction.
%gives the remainder and only accepts integers; the result takes the sign of the left operand.- Comparisons produce a bool;
&&and||short-circuit, so put the guard on the left. =assigns and is an expression;==compares. Mixing them compiles and misbehaves.++xyields the new value,x++the old one; on a line by itself either is fine.- Compound assignments such as
+=and%=update a variable in place. - Precedence: unary, then
* / %, then+ -, then comparisons, then&&, then||, then assignment. Parentheses when in doubt.
Try it yourself
The program reads a duration in seconds and prints it unchanged. Using / and %, make it print the duration as hours, minutes and seconds in the form 1h 1m 5s for the input 3665.
#include <iostream>
int main() {
int seconds;
std::cin >> seconds;
std::cout << seconds << "s\n";
return 0;
}
36651h 1m 5s#include <iostream>
int main() {
int seconds;
std::cin >> seconds;
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int rest = seconds % 60;
std::cout << hours << "h " << minutes << "m " << rest << "s\n";
return 0;
}
Practise this
Exercises for this lesson are in the C++ practice set.
Related lessons
- Variables and Types in C++Declaring C++ variables with int, double, char, bool and std::string, initialising them, using const and auto, and why an uninitialised variable is a bug.9 min
- Conditions in C++: if, else and switchHow C++ chooses between paths with if, else if and else, switch with break, the ?: operator and the C++17 if with an initialiser, with runnable examples.8 min
Frequently asked questions
Why does 7 / 2 give 3 in C++?
Because both operands are ints, and the division of two ints is defined to produce an int, truncating toward zero. The type of the variable receiving the result does not matter; the truncation has already happened. Write 7 / 2.0, or convert a variable with static_cast<double>, to get 3.5.
What is the difference between i++ and ++i?
Both add 1 to i. The expression ++i produces the value after the increment; i++ produces the value before it. When the value is not used, as in a loop step, they behave identically for ints. ++i is the habit worth forming, because for heavier types such as iterators the postfix form has to keep a copy of the old value.
Does C++ have a power operator?
No. The ^ symbol is bitwise exclusive-or, so 2 ^ 3 is 1, not 8. Use std::pow(2, 3) from <cmath>, which returns a double, or multiply in a loop when you need an exact integer result.
How this page was checked. Every program on it was run with GCC 13 (C++17) at build time by the publishing checks, and the output shown is what it printed. Running C++ inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with GCC 13 (C++17) locally.