The C Path — learn C, visually

🌱 C Basics

Comparisons & logic: how C decides

⏱ 13 min · free interactive lesson · quizzes, visualizations & a real compiler

▶ Open the interactive lesson — free, no signup
Why you're learning this

One missing character — typing = where you meant == — can turn a password check into one that always says yes. Comparisons are how programs decide anything at all, and by the end of this lesson you'll recognize that one-character bug on sight and know how to make the compiler catch it for you.

Every decision a program makes — checking a password, deciding whether to keep going — boils down to a comparison. C's comparison operators are == != < > <= >=, and here's the twist: they don't produce some special true/false type of value. They return a plain int1 for true, 0 for false.

compare.c
#include <stdio.h>

int main(void) {
    int x = 7, y = 10;

    printf("x == y : %d\n", x == y);
    printf("x != y : %d\n", x != y);
    printf("x <  y : %d\n", x <  y);
    printf("x >= y : %d\n", x >= y);

    int adult = 1, has_ticket = 0;
    printf("adult && has_ticket : %d\n", adult && has_ticket);
    printf("adult || has_ticket : %d\n", adult || has_ticket);
    printf("!has_ticket         : %d\n", !has_ticket);
    return 0;
}
terminal
$ gcc compare.c -o compare && ./compare
x == y : 0
x != y : 1
x <  y : 1
x >= y : 0
adult && has_ticket : 0
adult || has_ticket : 1
!has_ticket         : 1

Truthiness: zero is false, everything else is true

Going the other way, wherever C expects a condition, it applies one rule: 0 means false; any nonzero value means true. -3 is true. 0.5 is true. 'A' (which is 65) is true. This simple rule powers idioms you'll see everywhere, like if (count) meaning "if count isn't zero".

🧠 Checkpoint: In a C condition, which of these values counts as TRUE?

  • 0
  • 0.0
  • -1
  • '\0'
Show answer

-1 — The rule is brutally simple: zero (in any type) is false, everything else is true. −1 is nonzero, so it’s true. '\0' is the character with value 0 — false.

The bug that launched a thousand debugging sessions: = vs ==

the-bug.c — spot it before running
int logged_in = 0;

/* meant to check, actually assigns! */
if (logged_in = 1) {
    printf("Welcome back, admin!\n");   /* runs EVERY time */
}
💀

= assigns, == compares — and both are valid in a condition. if (x = 0) assigns 0 to x, and the assignment's value (0) makes the condition false — always. if (x = 5) is always true. No error, just silently wrong logic. Compile with -Wall (GCC suggests extra parens when you really mean assignment) and this bug can't hide.

Combining conditions: &&, ||, !

And they hide a superpower: short-circuit evaluation. C evaluates left to right and stops as soon as the answer is known. If the left side of && is false, the right side is never evaluated at all. Watch calls in this trace — it counts how many times noisy() actually runs:

This spot has an interactive trace widget — open the interactive lesson to play with it.

This isn't just an optimization — it's a guarantee you can lean on. The classic idiom if (n != 0 && total / n > 10) is safe: the division simply cannot happen when n is zero.

🧠 Checkpoint: In if (p != NULL && *p > 0), what happens when p is NULL?

  • Crash — *p is still evaluated
  • The whole condition is false; *p is never touched
  • Compile error
  • Undefined behavior
Show answer

The whole condition is false; *p is never touched — Short-circuit && guarantees left-to-right evaluation with an early exit: once p != NULL is false, the dereference on the right is skipped. This guard pattern is everywhere in real C.

The ternary operator: ?:

C's only three-operand operator is an if/else that fits inside an expression: condition ? value_if_true : value_if_false.

ternary.c
#include <stdio.h>

int main(void) {
    int a = 12, b = 30;

    int max = (a > b) ? a : b;
    printf("max is %d\n", max);
    printf("b is %s\n", (b % 2 == 0) ? "even" : "odd");
    return 0;
}
terminal
$ gcc ternary.c -o ternary && ./ternary
max is 30
b is even
💡

Ternaries shine for small choices — a max, a label, a sign. Nest them twice and readability dies. If it doesn't fit comfortably on one line, use a real if.

This spot has an interactive editor widget — open the interactive lesson to play with it.

🧠 Checkpoint: What does x = (5 > 3) ? 10 : 20; assign?

  • 10
  • 20
  • 1
  • 5
Show answer

10 — 5 > 3 evaluates to 1 (true), so the ternary yields its first branch: 10. The second branch, 20, is not even evaluated.

Comparisons treat variables as whole values — but you can also reach inside them and flip individual bits. Next: the bitwise operators.

▶ Practice this lesson interactively (with live gcc)