C from the ground up

Decisions with if and else

Make a program choose a path: the if statement, the comparison operators, and the difference between one equals sign and two.

Part 3 of 12

Beginner12 min readBack to Courses

Choosing a path

An if statement runs a block of code only when a condition is true. The condition goes in round brackets, and the code it guards goes in curly braces.

Add else for the other case, or else if to test a second condition when the first fails. The first branch whose condition is true runs, and the rest are skipped.

Comparing values

A condition is built from comparison operators: < less than, > greater than, <= at most, >= at least, == equal, and != not equal. Each one answers a yes or no question.

The trap is == against =. Two equals signs compare two values. A single equals sign assigns, which is a different thing, and the compiler may not warn you. Read carefully.

Combining and nesting

You can join conditions with && meaning "and" and || meaning "or". A condition can also sit inside another statement, so decisions nest as deeply as the problem needs.

Keep branches short and each one readable. When a single if grows past a few lines it is usually a sign that a helper function would say the same thing more clearly.

When one value has many cases

A switch compares one whole-number or character value against a list of case labels. Each label is a constant, not a range, and the value is tested once rather than once per branch. That makes it a tidy fit for a menu choice or a grade letter, where the alternatives are distinct values rather than comparisons.

break ends the case; without it, execution falls into the next label. That fallthrough is occasionally deliberate, when several labels share one piece of work, but it is a bug the rest of the time. default catches everything that matched no label, and a switch on a value with no case and no default simply does nothing.

Boundaries are where the bugs live

Every range test has two edges, and each edge belongs to exactly one branch. In a chain that awards A at 90 and B at 70, a score of 90 must not also pass the B test, and a score of 69 must not fall through both. Read the chain with the boundary values in hand: 89, 90, 69 and 70.

A gap leaves a value that matches nothing; an overlap lets the first branch steal a case the later one was written for. Both are invisible in a quick read because the numbers look reasonable. Write the edges down, then trace one value from each side of every edge before you trust the chain.

What counts as true

C does not have a separate boolean type in its original form: a condition is an integer, and zero is false while every non-zero value is true. That is why if (count) means "if count is not zero" and if (!done) means "if done is zero".

The operators && and || stop as soon as the answer is known. In if (p != NULL && p->value > 0), the second half is not evaluated when p is NULL, which is exactly the guard that keeps the dereference safe.

Three habits prevent most conditional bugs: put the constant on the left when comparing (if (5 == x) turns a stray = into a compile error), always use braces even for a one-line branch, and never compare floating-point values with == — use a tolerance instead.

A single choice

#include <stdio.h>

int main(void) {
    int score = 72;

    if (score >= 60) {
        printf("Pass\n");
    } else {
        printf("Try again\n");
    }

    return 0;
}

The conditional operator and early exits

C has a compact expression for a two-way choice: condition ? when_true : when_false. It is an expression, so it can sit inside a printf or an assignment. Use it for a small value, not for branches with side effects; a full if is clearer when the work is a statement.

A guard clause returns early when a precondition fails, which keeps the happy path unindented. In C that often means checking a pointer before using it: if (p == NULL) return; at the top of a function is easier to follow than a deep nest of if (p != NULL) blocks.

Try these next

  • Rewrite the grade chain as a switch and check that each case still prints once.
  • Use condition ? "open" : "closed" inside a printf for the office-hours example.
  • Add a null-pointer guard to a small function and call it with NULL to see the early return.

Grade a score

Run it, then change the score to 91 and to 40 and predict which branch runs each time before you press Run.

Output
Click "Run" to execute your code

A switch, and a guarded condition

Run it, then change the grade to `C` and the hour to 18. Predict which messages appear before you run it again.

Output
Click "Run" to execute your code

Writing a single equals sign where you meant two is the classic C slip. If a condition seems to run no matter what, check for a stray assignment first.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 6

Which operator compares two values for equality?