C++, End to End
Operators
C++ has roughly forty operators, and almost all of them behave exactly the way you'd expect from other languages. This chapter covers the ones worth pausing on: the ones with a real gotcha attached.
Arithmetic operators
+ - * / % // addition, subtraction, multiplication, division, modulo (remainder)Integer division truncating toward zero (not toward negative infinity) is the single most common arithmetic surprise for anyone coming from a language that rounds division differently. If you need real division between two ints, cast at least one operand to a floating-point type first.
Relational and logical operators
== != < > <= >= // relational: produce bool
&& || ! // logical AND, OR, NOTShort-circuiting isn't just an optimization detail, real code depends on it being guaranteed. `ptr != nullptr && ptr->value > 0` is only safe because the language guarantees the right side never runs if the pointer is null; if it weren't guaranteed, that pattern would be undefined behavior half the time.
Bitwise operators
& // AND
| // OR
^ // XOR
~ // NOT (unary, flips every bit)
<< // left shift
>> // right shiftThese operate on the actual bit pattern of an integer, and this course has a full dedicated chapter on bit manipulation, since it's genuinely its own topic (flags, masks, packing multiple values into one integer). The one thing worth flagging immediately here: & and | are NOT shorthand for && and ||. Using a single & where you meant && still compiles, since it's a perfectly legal (different) operator, and the resulting bug can be very hard to spot by eye.
Operator precedence and associativity
Precedence decides which operator binds tighter when two appear next to each other without parentheses; associativity decides the order when several operators of the same precedence appear in a row. C++'s precedence table has more than fifteen levels, and nobody has it fully memorized. The practical answer is: when in doubt, parenthesize. It costs nothing and removes the ambiguity for both the compiler and the next person reading the code.
The comma operator
The comma operator evaluates its left operand, discards the result, then evaluates and returns its right operand. It shows up almost exclusively in the increment/update clause of a for loop, and almost anywhere else it's a sign the code should be two statements instead of one expression.
Careful: a comma inside function-call parentheses is argument separation, not the comma operator. `f(a, b)` calls f with two arguments; it never invokes the comma operator at all. The comma operator only applies where a single expression is expected and a literal comma appears inside it, which in practice is rare outside that for-loop pattern.
The ternary operator
condition ? valueIfTrue : valueIfFalseThe ternary operator is an expression, not a statement, which is exactly why it can appear inside another expression (as an initializer, as a function argument) where an if/else block flatly cannot. That's its actual justification, not just "shorter than if/else." Nesting more than one ternary in a single expression reliably makes code harder to read than the equivalent if/else chain would have been; resist the urge.
Try it yourself: predict the output of `10 - 2 - 3` and `2 + 3 == 5 && 1` on paper first (subtraction and equality both have associativity/precedence rules worth confirming you actually know), then run them and check. The second one is a genuine trap: == binds tighter than &&, so it parses as `(2 + 3 == 5) && 1`, not something weirder.