Operators
Operators are the symbols that do the actual work on your variables — arithmetic, comparisons, and the logic that decides what a program does next. Most of them behave exactly the way you'd expect from arithmetic class; the one that trips people up is division, because C++ treats it differently depending on the types involved.
Arithmetic and the integer division trap
The five arithmetic operators are +, -, *, /, and % (remainder). When both operands of / are integers, C++ performs integer division — it throws away any fractional part rather than rounding:
#include <iostream> int main() { int a = 17, b = 5; std::cout << "a + b = " << a + b << std::endl; std::cout << "a - b = " << a - b << std::endl; std::cout << "a * b = " << a * b << std::endl; std::cout << "a / b = " << a / b << std::endl; std::cout << "a % b = " << a % b << std::endl; return 0; }
a + b = 22 a - b = 12 a * b = 85 a / b = 3 a % b = 2
17 divided by 5 is 3.4, but a / b printed 3 — the compiler saw two int operands and produced an int result, dropping everything after the decimal point rather than rounding to the nearest whole number. a % b gives you back that dropped remainder: 5 goes into 17 three times with 2 left over. If you actually want the fractional answer, at least one operand needs to be a floating-point type — you can force that with static_cast:
#include <iostream> int main() { int a = 17, b = 5; double result = static_cast<double>(a) / b; std::cout << "Precise division: " << result << std::endl; return 0; }
Precise division: 3.4
static_cast<double>(a) converts just a to a double before the division happens; because one operand is now a double, C++ promotes b to a double too and performs floating-point division on both. This is one of the most common real bugs in C++ code — a percentage or an average silently truncated to a whole number because both sides of a division happened to be integers.
Increment, decrement, and compound assignment
Rather than writing score = score + 5, C++ lets you write score += 5; every arithmetic operator has a compound form (-=, *=, /=, %=). For adding or subtracting exactly one, there's a further shorthand: ++ and --. Where they get interesting is that each comes in two flavors — count++ ("post-increment") returns the value before incrementing, while ++count ("pre-increment") increments first and returns the new value:
#include <iostream> int main() { int score = 10; score += 5; score *= 2; std::cout << "Score: " << score << std::endl; int count = 0; std::cout << "count++ gives " << count++ << std::endl; std::cout << "now count is " << count << std::endl; std::cout << "++count gives " << ++count << std::endl; return 0; }
Score: 30 count++ gives 0 now count is 1 ++count gives 2
In a bare statement like count++; on its own line, the two forms behave identically — the difference only shows up when you use the expression's result directly, as in the middle of a cout chain or a loop condition. In C-style for loops you'll see i++ used out of habit far more than the distinction is actually needed.
Relational and logical operators
Comparisons (==, !=, <, >, <=, >=) always produce a bool. You combine them with && (and), || (or), and ! (not) to build up more complex conditions:
#include <iostream> int main() { int age = 20; bool hasLicense = true; bool canRentNoSurcharge = (age >= 21) && hasLicense; bool canRentWithSurcharge = (age >= 18) && hasLicense; std::cout << "Can rent without surcharge: " << canRentNoSurcharge << std::endl; std::cout << "Can rent with surcharge: " << canRentWithSurcharge << std::endl; return 0; }
Can rent without surcharge: 0 Can rent with surcharge: 1
At 20, the renter fails the age >= 21 check, so canRentNoSurcharge is false (printed as 0); they do clear the lower age >= 18 bar, so canRentWithSurcharge comes out true. && and || also short-circuit — if the left side of && is already false, C++ never bothers evaluating the right side, since the answer can't change. That's not just an optimization; it's safe to rely on, and it's why you'll often see a null-check written as ptr != nullptr && ptr->isValid() — the second check only runs once the first has confirmed it's safe to.
= (assignment) with == (comparison). if (score = 100) compiles — it assigns 100 to score and then evaluates the assignment's result, which is truthy — but it almost certainly isn't what you meant. Most compilers will warn about this if you enable warnings with -Wall, which is worth turning on for exactly this reason.