Expressions are the bulk of any program, and a few C++ operator rules (precedence, signed/unsigned mixing) cause classic bugs.
The arithmetic operators are + - * / %, with compound forms like += and *=. % is the remainder; with negative operands division truncates toward zero, so -7 / 2 is -3 and -7 % 2 is -1.
++i increments and yields the new value; i++ yields the old value. On their own as statements they're equivalent; prefer ++i as a habit (for iterators it can be cheaper).
Comparisons (== != < <= > >=) yield bool. The logical operators &&, || and ! short-circuit: in count != 0 && total / count > 10 the division is never evaluated when count is 0. That is guaranteed and widely relied upon.
The bitwise operators & | ^ ~ << >> work on the bits of integers; use unsigned types for bit manipulation. Watch precedence: == binds tighter than &, so flags & mask == 0 means flags & (mask == 0). When in doubt, add parentheses.
The conditional operator cond ? a : b picks one of two values and is handy for initializing const variables.
Mixed-type expressions are converted to a common type. The nasty case is signed with unsigned: the signed value is converted to unsigned, so -1 < 0u is false because -1 becomes 4294967295. Compilers warn about this (-Wall enables -Wsign-compare); C++20 adds std::cmp_less and friends in <utility> that compare mixed integers correctly. This bites most often when comparing an int index with v.size().
Finally, = assigns and == compares. if (x = 5) compiles (with a warning) and is always true.
Example
#include <iostream>
#include <utility>
int main() {
int a = 17, b = 5;
std::cout << a / b << ' ' << a % b << '\n'; // 3 2
std::cout << -7 / 2 << ' ' << -7 % 2 << '\n'; // -3 -1 (truncates toward zero)
int i = 5;
int x = i++; // x = 5, then i = 6
int y = ++i; // i = 7, then y = 7
std::cout << x << ' ' << y << '\n'; // 5 7
int count = 0, total = 50;
if (count != 0 && total / count > 10) { } // short-circuit: no division by zero
unsigned flags = 0b1010;
bool bit1 = (flags & 0b0010) != 0; // parentheses needed: != binds tighter
std::cout << bit1 << ' ' << (flags >> 1) << '\n'; // 1 5
std::cout << (-1 < 0u) << '\n'; // 0 (!) -1 converts to a huge unsigned
std::cout << std::cmp_less(-1, 0u) << '\n'; // 1, C++20 safe comparison
int score = 72;
const char* grade = score >= 60 ? "pass" : "fail";
std::cout << grade << '\n'; // pass
}Remember
&&and||short-circuit, which makes guards likep && p->oksafe- Comparing signed with unsigned converts the signed side:
-1 < 0uis false ==binds tighter than&,|,^: parenthesise bit tests
Watch out for
- Comparing
int iwithv.size()when i can be negative — usestd::ssize(v)orstd::cmp_less.