student@ubuntu:~$
c-foundations Lesson 4 9 min read

Operators & Expressions

Arithmetic, comparison, logical, and bitwise operators — plus the = vs == trap

Reading: Hanly & Koffman: §2.5 (pp. 72–86), §4.2 (pp. 175–184)

Quick check before you start: Do you know what ^ does in C? (Hint: it is not exponentiation.) If not, read on. If you can explain it, skip to Precedence.

Practice this topic: C Operators skill drill

After this lesson, you will be able to:

  • Use arithmetic, comparison, and logical operators in expressions
  • Apply bitwise operators for bit-level manipulation
  • Use sizeof to determine the size of a type or variable
  • Explain why = and == are different and how confusing them causes bugs

Arithmetic Operators

Same as Java, with one critical difference: integer division truncates.

int a = 7, b = 3;

printf("%d\n", a + b);   /* 10 */
printf("%d\n", a - b);   /* 4  */
printf("%d\n", a * b);   /* 21 */
printf("%d\n", a / b);   /* 2, not 2.333 */
printf("%d\n", a % b);   /* 1  */

Increment and decrement work the same way: i++ (post), ++i (pre), i--, --i. In standalone statements, the difference does not matter. In expressions, it does.

Comparison & Logical Operators

C has no boolean type (before C99). Zero is false, and any non-zero value is true.

int x = 5, y = 10;

printf("%d\n", x < y);    /* 1 (true)  */
printf("%d\n", x == y);   /* 0 (false) */
printf("%d\n", x != y);   /* 1 (true)  */

printf("%d\n", x > 0 && y > 0);   /* 1 — logical AND */
printf("%d\n", x > 0 || y < 0);   /* 1 — logical OR  */
printf("%d\n", !(x > 0));         /* 0 — logical NOT */

Key Insight: C uses short-circuit evaluation. In a && b, if a is 0, b is never evaluated. In a || b, if a is non-zero, b is never evaluated. This matters when b has side effects.

Bitwise Operators

These operate on individual bits. You will use them in systems programming and CTF challenges.

Operator Name Example Result
& AND 0b1100 & 0b1010 0b1000
\| OR 0b1100 \| 0b1010 0b1110
^ XOR 0b1100 ^ 0b1010 0b0110
~ NOT ~0b1100 0b...0011
<< left shift 1 << 3 8
>> right shift 16 >> 2 4
unsigned char flags = 0;

flags |= (1 << 2);    /* set bit 2:   flags = 00000100 */
flags |= (1 << 5);    /* set bit 5:   flags = 00100100 */

if (flags & (1 << 2)) {
    printf("Bit 2 is set\n");
}

flags &= ~(1 << 2);   /* clear bit 2: flags = 00100000 */

Left shift by n multiplies by 2^n. Right shift divides by 2^n (for unsigned values).

sizeof

sizeof returns the size in bytes of a type or variable. It is evaluated at compile time, not runtime.

printf("int: %zu bytes\n", sizeof(int));       /* 4 */
printf("double: %zu bytes\n", sizeof(double)); /* 8 */
printf("char: %zu bytes\n", sizeof(char));     /* 1 */

int arr[10];
printf("arr: %zu bytes\n", sizeof(arr));       /* 40 */

Use %zu for sizeof results — the return type is size_t, an unsigned integer.

The = vs == Trap

This is the single most common C bug for beginners.

int x = 5;

if (x = 0) {             /* BUG: assigns 0 to x, then tests 0 (false) */
    printf("zero\n");
}

if (x == 0) {            /* CORRECT: compares x to 0 */
    printf("zero\n");
}

= is assignment. == is comparison. The assignment x = 0 evaluates to 0, which is false. So the if-body never runs, and x is now 0. The compiler may warn you, but it is legal C. Some programmers write 0 == x (Yoda conditions) to catch this — 0 = x would be a compiler error.

Operator Precedence

You do not need to memorize the full table. Know these rules:

  1. Unary (!, ~, ++, --, (type)) before binary
  2. Arithmetic (*, /, %) before (+, -)
  3. Shift (<<, >>) before comparison
  4. Comparison (<, >, <=, >=) before equality (==, !=)
  5. Bitwise (& before ^ before |)
  6. Logical (&& before ||)
  7. Assignment (=, +=, etc.) is nearly last

When in doubt, use parentheses. (a + b) * c is clearer than relying on precedence.


Check Your Understanding
What does if (x = 5) do in C?
ACompares x to 5 and enters the if-body when they are equal
BAssigns 5 to x, then evaluates as true because 5 is non-zero
CCauses a compilation error
DAssigns 5 to x and evaluates as false
Answer: B. The single = is the assignment operator. x = 5 stores 5 in x and the expression evaluates to 5. Since 5 is non-zero, C treats it as true, so the if-body always executes. This is almost never what you want. Use == for comparison.

What Comes Next

You have the building blocks — variables, I/O, and operators. Next, you will combine them into decision-making and loops with if/else, switch, and the three loop forms.