Operators & Expressions

Operators combine values into expressions, and expressions are the building blocks that statements are made of. Understanding precedence, associativity, and the expression/statement distinction avoids a surprising number of subtle bugs.

An operator performs an operation on one or more values (operands) — + adds, == compares for equality, && performs logical AND. An expression is any combination of values and operators that evaluates to a single value (like 3 + 4). A statement is a complete instruction that performs an action (like an assignment or a function call) but doesn't necessarily produce a usable value itself.

Operators let you build up complex calculations and comparisons from simple pieces. Distinguishing expressions from statements matters because expressions can be nested and combined (used as part of a larger expression), while many statements cannot be.

Every calculation, comparison, and logical decision in every program is built from operators and expressions — a shopping cart's total price, a login form's password check, and a game's collision detection all boil down to operator expressions under the hood.

Think of expressions like arithmetic on a calculator — you can combine 3 + 4 * 2 into one bigger expression, and the calculator evaluates it down to a single number, respecting the standard order of operations. A statement is more like pressing the calculator's 'clear' or 'print' button — it does something, but doesn't itself hand you a number to use in a further calculation.

(3 + 4) * 2 is an expression — it evaluates to the value 14, which is then stored by the assignment statement. print(result) is a statement performing an action (printing); its 'result' isn't itself usable as a value in Python.

Operator precedence determines which operator in a mixed expression is evaluated first (multiplication before addition, for example); operator associativity determines the order when operators of the same precedence appear together (most binary operators are left-to-right, but assignment and exponentiation are typically right-to-left).

Common operator categories: arithmetic (+ - * / %), comparison/relational (== != < > <= >=), logical (&& || ! or and/or/not), bitwise (& | ^ ~ << >>), and assignment (= += -= etc.).

Short-circuit evaluation means logical && and || (and their keyword equivalents) don't always evaluate their right-hand operand — if the left side of && is false, or the left side of || is true, the overall result is already determined, and many languages skip evaluating the right side entirely.

Some languages blur the expression/statement line deliberately — in many languages (Rust, and to a lesser degree JavaScript's arrow functions and ternary operator), most constructs are expressions that produce a value, even things that look like control flow, whereas classic C-style languages keep a stricter separation.

Relying on assumed precedence instead of parentheses in a genuinely ambiguous-looking expression, making the code hard for a reader (or your future self) to verify correctly, even if the compiler evaluates it exactly as intended.

Using = (assignment) where == (comparison) was intended inside a conditional, in languages that permit it (classic C) — this compiles and can silently do the wrong thing, since an assignment expression evaluates to the assigned value.

Forgetting that short-circuit evaluation means a function call on the right side of && or || might never execute, if relied upon for a necessary side effect.

Mixing up bitwise operators (& |) with logical operators (&& ||) — they look similar but behave very differently, especially on non-boolean operands.

Using short-circuit evaluation deliberately as a guard: if (obj != null && obj.isValid()) safely avoids calling isValid() on a null object, because obj.isValid() is never evaluated when obj != null is already false.

Using parentheses generously in complex conditional expressions purely for human readability, even when the default precedence would already produce the correct result.

Choosing bitwise operators specifically for flag manipulation (setting/clearing/checking individual bits) in embedded and systems programming, where each bit represents a distinct hardware or status flag.

Expressions compile to instruction sequences An expression like (3 + 4) * 2 doesn't exist as a single unit at the machine-code level — a compiler decomposes it into a sequence of individual arithmetic instructions (load, add, multiply, store), often reordering or combining them during optimization as long as the observable result stays the same, a topic covered fully in the future Compiler Optimization module.

Sequence points and evaluation order In some languages (notoriously C and C++ prior to certain standard revisions), the order in which the operands of certain expressions are evaluated is unspecified or was historically undefined when the same variable is both read and modified within one expression (like i = i++ + ++i) — leading to genuinely different results across compilers. Modern language designs increasingly specify evaluation order explicitly to avoid this entire class of ambiguity.

Bitwise operators and hardware-level control Bitwise operations map almost directly to single CPU instructions and are the primary tool for manipulating hardware registers, flags, and packed binary protocols in embedded and systems programming — a topic that recurs throughout the future Embedded Programming and Computer Architecture modules.

What is short-circuit evaluation?

The behavior where a logical operator (&&/and, ||/or) skips evaluating its right-hand operand once the overall result is already determined by the left-hand operand — for &&, if the left side is false; for ||, if the left side is true.

What's the difference between an expression and a statement?

An expression evaluates to a value and can typically be nested inside other expressions. A statement performs an action (like an assignment, a loop, or a function call) and, in many languages, does not itself produce a reusable value.

Output-based: in a C-like language, what's the danger of writing if (x = 5) instead of if (x == 5)?

= is assignment, not comparison — x = 5 assigns 5 to x and the expression itself evaluates to 5 (truthy), so the if block executes regardless of x's previous value, silently overwriting x in the process — a classic and hard-to-spot bug.

What's the difference between & and && in most C-family languages?

& is the bitwise AND operator, comparing operands bit by bit (and always evaluating both operands). && is the logical AND operator, treating operands as booleans and short-circuiting — skipping the right operand entirely if the left is already false.

result = (3 + 4) * 2   # expression: (3 + 4) * 2, evaluates to 14
print(result)            # statement: performs an action, produces no value to reuse