What Are Operators and How Do I Use Them in Expressions?
Arithmetic, comparison, logical, bitwise — and the traps that catch Java programmers
After this lesson, you will be able to:
- Identify the bug in
if (x = 5)versusif (x == 5) - Use arithmetic, comparison, and logical operators in C expressions
- Explain C’s truthiness rule: zero is false, any non-zero value is true
- Trace bitwise operations (
&,|,^,~,<<,>>) on binary representations - Distinguish between bitwise (
&,|) and logical (&&,||) operators - Use
sizeofto determine the byte size of a data type or variable
The = vs == Trap
Pop quiz. What does this C code do?
int x = 5;
if (x = 3)
{
printf("True!\n");
}
If you said “checks if x equals 3” — you just fell into C’s most famous trap. That’s assignment (=), not comparison (==). It sets x to 3, evaluates to 3 (non-zero = true), and always prints “True!”
In Java, this would be a compile error. In C, it compiles silently. Welcome to operators in C.
if (x = 5) do in C?= is assignment, not comparison. The expression x = 5 assigns 5 to x and evaluates to 5. Since 5 is non-zero, C treats it as true — the if-body always runs. The programmer almost certainly meant x == 5. Compile with -Wall to catch this.
Every Operator You Need
Arithmetic (Familiar from Java)
int a = 17, b = 5;
printf("%d\n", a + b); // 22
printf("%d\n", a - b); // 12
printf("%d\n", a * b); // 85
printf("%d\n", a / b); // 3 (integer division!)
printf("%d\n", a % b); // 2 (remainder/modulo)
Increment and decrement:
int count = 5;
count++; // count is now 6
count--; // count is now 5 again
Assignment shortcuts:
x += 5; // x = x + 5
x -= 3; // x = x - 3
x *= 2; // x = x * 2
x /= 4; // x = x / 4
x %= 3; // x = x % 3
These all work the same as Java.
Comparison Operators
x == y // Equal to
x != y // Not equal to
x < y // Less than
x > y // Greater than
x <= y // Less than or equal
x >= y // Greater than or equal
Common Pitfall:
=is assignment,==is comparison.if (x = 5)assigns 5 to x and is always true.if (x == 5)checks if x equals 5. Always compile with-Wall— gcc warns about assignment inside conditions.
Logical Operators
x && y // AND — true if both true
x || y // OR — true if either true
!x // NOT — true if x is false
Key Insight: In C, truth is numeric: zero is false, any non-zero value is true. There’s no separate
booleantype in C89 — conditions are just integer expressions.if (x)means “if x is not zero.”if (!x)means “if x is zero.”
int x = 5; if (x = 0) { printf("yes"); } else { printf("no"); }= vs == trap. The expression x = 0 assigns 0 to x and evaluates to 0. Since 0 is false in C, the else branch runs. The programmer almost certainly meant x == 0. This compiles without error in C (unlike Java), which is why -Wall is essential — it warns about assignment in conditions.
This means unusual expressions are valid:
int count = 42;
if (count) // True! (42 is non-zero)
{
printf("count has a value\n");
}
Bitwise Operators
These operate on individual bits — new territory for most Java programmers:
| Operator | Name | Example | Result |
|---|---|---|---|
& |
AND | 5 & 3 |
1 |
\| |
OR | 5 \| 3 |
7 |
^ |
XOR | 5 ^ 3 |
6 |
~ |
NOT | ~5 |
-6 |
<< |
Left shift | 1 << 3 |
8 |
>> |
Right shift | 16 >> 2 |
4 |
Let’s trace 5 & 3:
5 = 0101
& 3 = 0011
--------
1 = 0001
Key Insight: This is exactly how Unix file permissions work internally. The 9 permission bits (
rwxrwxrwx) are stored as a bitmask.chmod 755sets bits111 101 101. When the OS checks “can group execute?”, it ANDs the permission mask with the group-execute bit.
Common Pitfall: Don’t confuse bitwise operators with logical operators!
&(bitwise AND) is very different from&&(logical AND).5 & 3is1.5 && 3is1(true). They happen to give the same result here, but2 & 4is0while2 && 4is1.
Why does this matter?
Bitwise operations are how hardware actually works. Every CPU instruction, every network packet, every file permission is bits being ANDed, ORed, and shifted. When you write systems code in Week 10, you’ll use bitwise operators to set flags, check permissions, and manipulate data at the lowest level.
2 & 4 vs 2 && 4?2 is 010, 4 is 100 — no bits overlap, so 2 & 4 = 000 = 0. Logical: both 2 and 4 are non-zero (true), so 2 && 4 = 1 (true). Option D confuses & (AND) with | (OR) — 2 | 4 would be 6.
Deep dive: Bitwise operators and Unix permissions — seeing the connection
In Lesson 1.4, you learned that chmod 755 sets permissions to rwxr-xr-x. Now you can see how that actually works at the bit level:
Octal 7 = binary 111 = rwx (read + write + execute)
Octal 5 = binary 101 = r-x (read + execute, no write)
Octal 5 = binary 101 = r-x
So 755 = 111 101 101 in binary
When the OS checks “can the group write to this file?”, it performs a bitwise AND:
mode_t permissions = 0755; // File permissions (octal literal)
mode_t group_write = 0020; // Group write bit mask
if (permissions & group_write) // 0755 & 0020 = 0?
{
// Group can write
}
else
{
// Group cannot write (this branch runs for 755)
}
0755 = 111 101 101
& 0020 = 000 010 000
─────────────────
0000 = 000 000 000 → zero → false → no group write
This is why permission values are powers of 2 (4=read, 2=write, 1=execute) — each corresponds to a single bit position, so you can AND, OR, and check individual permissions without affecting others:
permissions |= 0020; // Grant group write (set the bit)
permissions &= ~0020; // Revoke group write (clear the bit)
This bit-manipulation pattern — set, clear, and test individual flags using AND, OR, and NOT — is one of the most fundamental patterns in systems programming.
The sizeof Operator
sizeof tells you how many bytes a type or variable uses:
printf("int: %zu bytes\n", sizeof(int)); // 4
printf("double: %zu bytes\n", sizeof(double)); // 8
printf("char: %zu bytes\n", sizeof(char)); // 1
The Trick: You’ll use
sizeofconstantly withcallocfor dynamic memory allocation:int *arr = calloc(n, sizeof(int));. It ensures you allocate the right number of bytes regardless of the platform.
Type Casting
Explicit conversion between types:
int total = 7;
int count = 2;
double average = (double)total / count; // 3.5, not 3
Implicit conversions happen automatically when mixing types:
int x = 5;
double y = x; // Implicit: int → double (safe, no data loss)
int z = 3.14; // Implicit: double → int (truncates! z = 3)
The Trick: When computing an average, cast the numerator:
(double)sum / count. Casting either operand promotes the entire expression to floating-point division.
Operator Precedence
When in doubt, use parentheses. But here’s the short version:
| Priority | Operators |
|---|---|
| Highest | () function calls, [] array index |
! ~ ++ -- (unary) |
|
* / % |
|
+ - |
|
<< >> |
|
< <= > >= |
|
== != |
|
& ^ \| |
|
&& \|\| |
|
| Lowest | = += -= etc. |
Watch out: Bitwise operators have lower precedence than comparison operators.
a & b == cparses asa & (b == c), which is almost never what you want. Always use parentheses:(a & b) == c. This is one of C’s most notorious precedence traps.
sizeof(char) always return in C?sizeof(char) as exactly 1 byte. This is the one type whose size is guaranteed. Other types like int and long can vary by platform. Java's char is 2 bytes (Unicode), but C's char is always 1 byte (ASCII).
Quick Check: What does if (x = 0) do?
It assigns 0 to x, then evaluates the condition. Since 0 is false, the if-body never runs. This is almost always a bug — the programmer meant if (x == 0). Compile with -Wall to catch this.
Quick Check: What's the difference between & and &&?
& is bitwise AND — it operates on individual bits of integers. && is logical AND — it evaluates to 1 (true) if both operands are non-zero. Example: 6 & 3 = 2 (bitwise), 6 && 3 = 1 (logical).
Quick Check: Why does if (count) work as a condition?
In C, any non-zero value is true. if (count) is equivalent to if (count != 0). This is idiomatic C — you’ll see it in professional code everywhere.
Operators Are Your Vocabulary
Every C expression is built from operators. The ones that trip up Java programmers most: = vs ==, bitwise vs logical, and integer division. Now that you know the traps, you can avoid them.
Next: decisions. You’ll use these operators inside if, else if, and switch statements to make your programs branch. And you’ll meet C’s version of the dangling else problem.
Big Picture: Bitwise operators aren’t just academic. They show up in systems programming (file permissions, network protocols, hardware registers), embedded systems (controlling device pins), and performance optimization (fast multiplication by powers of 2 using shifts). You’ll use them in Week 10 when working with system calls.