← C++, End to End

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

c++
+  -  *  /  %   // addition, subtraction, multiplication, division, modulo (remainder)
c++ · live, editable, runnableOpen in Compiler Explorer ↗

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

c++
==  !=  <  >  <=  >=    // relational: produce bool
&&  ||  !               // logical AND, OR, NOT
c++ · live, editable, runnableOpen in Compiler Explorer ↗

Short-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

c++
&   // AND
|   // OR
^   // XOR
~   // NOT (unary, flips every bit)
<<  // left shift
>>  // right shift

These 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.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

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.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

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.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

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

c++
condition ? valueIfTrue : valueIfFalse
c++ · live, editable, runnableOpen in Compiler Explorer ↗

The 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.