In this lesson:
- the four families of operators you'll use every day
- the difference between
=(assignment) and==(equality), and why mixing them up is the most famous C beginner bug - short-circuit evaluation, and how it doubles as a safety check
- operator precedence, the order C applies operators when you don't use parentheses
- the assignment shortcuts (
+=,-=,++) that read better once you know them
Four families of operators
Operators are the glue between variables. C has many, but you can group the everyday ones into four families:
| Family | Operators | What they produce |
|---|---|---|
| Arithmetic | + - * / % |
A number |
| Relational | == != < > <= >= |
A truth value (0 or 1) |
| Logical | && || ! |
A truth value (0 or 1) |
| Assignment | = += -= *= /= %= |
The value being assigned |
That table fits on a Post-it but covers about 90% of expressions you'll ever write.
Arithmetic
The four basic ones are obvious; % (modulo) gives the remainder of integer division:
int a = 17;
int b = 5;
int sum = a + b; // 22
int diff = a - b; // 12
int prod = a * b; // 85
int quot = a / b; // 3 (integer division β truncates)
int rem = a % b; // 2 (17 = 3*5 + 2)Modulo is genuinely useful: n % 2 tells you if n is even or odd, index % BUFFER_SIZE wraps an index around a circular buffer, hour % 12 converts a 24-hour clock to 12-hour.
Relational and the == vs = trap
Relational operators compare two values and produce 1 if the comparison is true, 0 if false:
int x = 5;
int y = 10;
x < y // 1 (true)
x == y // 0 (false)
x != y // 1 (true)The famous beginner bug:
int score = 50;
if (score = 100) { // BUG: '=' assigns, doesn't compare
printf("Perfect!\n");
}That code always prints "Perfect!". The = assigns 100 to score and then evaluates to 100, which is non-zero, which counts as "true" in C. To compare, use ==:
if (score == 100) { // correct: '==' compares
printf("Perfect!\n");
}This bug is so common that compilers warn about it (-Wparentheses) and senior C programmers sometimes write the constant on the left, if (100 == score), so a typo (100 = score) becomes a compile error instead of a silent bug.
Logical operators and short-circuiting
&& (AND), || (OR), and ! (NOT) combine truth values:
int age = 25;
int has_ticket = 1;
if (age >= 18 && has_ticket) { // both must be true
printf("Let them in\n");
}
if (age < 18 || !has_ticket) { // either being true is enough
printf("Send them away\n");
}Short-circuit evaluation is the most important property of && and ||:
A && B, ifAis false,Bis not evaluated. The whole thing is already false.A || B, ifAis true,Bis not evaluated. The whole thing is already true.
This is not just a speed optimization. It is your shield against crashes:
// Safe: if 'p' is null, we never dereference it.
if (p != NULL && *p > 0) { ... }
// CRASH WAITING: '&' (bitwise AND) does not short-circuit. Both sides get
// evaluated, so *p runs even when p is NULL.
if (p != NULL & *p > 0) { ... }We will use this pattern constantly throughout the course.
Assignment, compound assignment, and increment
= is assignment, store the right side into the left side:
int count = 0;
count = count + 1; // works, but verboseC gives you compact shortcuts for "do something to a variable and store the result back":
count += 1; // count = count + 1
count -= 2; // count = count - 2
count *= 3; // count = count * 3
count /= 4; // count = count / 4
count %= 5; // count = count % 5And for the special case of adding or subtracting 1:
count++; // post-increment: use the value, then add 1
++count; // pre-increment: add 1, then use the value
count--; // post-decrement
--count; // pre-decrementFor a stand-alone statement like count++;, post and pre are identical in effect. The difference matters when you embed them inside a larger expression, we'll come back to this in the Loops lesson.
Precedence, the order C applies operators
When an expression mixes operators, C applies them in a fixed order (precedence) you don't have to memorize as long as you reach for parentheses:
int result = 2 + 3 * 4; // 14, not 20: * happens before +
int result2 = (2 + 3) * 4; // 20: parens force the addition firstA short cheat sheet for the families you've met:
Highest precedence
βββββββββββββββββββββββββββββ
! (unary NOT)
* / %
+ -
< <= > >=
== !=
&&
||
= += -= *= /= %=
βββββββββββββββββββββββββββββ
Lowest precedenceSo an expression like a == b && c < d parses as (a == b) && (c < d), which is what you wanted. But:
if (x & 0x01 == 0) // BUG: == has higher precedence than &parses as x & (0x01 == 0), almost certainly not what was intended. When in doubt, parenthesize. It costs you nothing and removes ambiguity.
Gotchas
=vs==.=is assignment.==is comparison.if (x = 0)is always false;if (x == 0)is what you meant.- Integer division and modulo with negatives. In C99+ both truncate toward zero, so
-7 / 2 == -3and-7 % 2 == -1, not-4and1. Don't assume Python-style behavior. &vs&&,|vs||. Single character is bitwise, double character is logical. The bitwise versions don't short-circuit and operate on every bit, not on truth values.- Precedence surprises.
==binds tighter than&and|.&&binds tighter than||. If you're mixing families, parenthesize.
TL;DR
- Four families: arithmetic, relational, logical, assignment. Learn one symbol from each before mixing.
=assigns,==compares, the most common C bug is using one when you wanted the other.&&and||short-circuit: if the result is already known, the right side never runs. Use this to writep != NULL && *p > 0safely.- Compound assignment (
+=,*=, etc.) and++/--are just shorthand, preferred for readability. - Use parentheses whenever an expression mixes operator families. They're free and they prevent bugs.