Skip to content
Lesson 6 of 13

Step 1 of 5 · Reading · ~4 min

Learn

Control Flow

Conditionals

By the end of this lesson you can make a C program choose exactly one of several actions, order the tests so the right one wins, and avoid the two C traps that compile silently: any number counts as a condition, and = fits where == belongs.

if, else if, else

int age = 67;
if (age < 12) {
    printf("child\n");
} else if (age >= 65) {
    printf("senior\n");
} else {
    printf("adult\n");
}

Output: senior. C tests each condition top to bottom and runs the first branch whose condition is true, then skips the rest of the chain. Parentheses around each condition are required. Braces are optional for one statement; write them anyway. Apple's 2014 goto fail bug was a brace-less if that gained a second line.

The final bare else is the catch-all: it runs when nothing above matched. Leave it off and the chain does nothing at all for those inputs: no output, no error.

Order decides the winner

When one condition is a special case of another, the special case must come first. The leap-year rule is the classic: a year divisible by 4 is a leap year, except years divisible by 100, except years divisible by 400.

int year = 1900;
if (year % 400 == 0) {
    printf("leap\n");
} else if (year % 100 == 0) {
    printf("common\n");
} else if (year % 4 == 0) {
    printf("leap\n");
} else {
    printf("common\n");
}

Trace for 1900: 1900 % 400 is 300, false. 1900 % 100 is 0, true, so it prints common and stops. Put the % 4 test first and 1900 (which is divisible by 4) prints leap, which is wrong, and the more specific tests never run. Rule: the narrowest condition goes at the top.

Remember from Numbers and printf that % is the remainder, so x % k == 0 means "x is a multiple of k". In C the remainder of a negative number is negative or zero (-7 % 4 is -3, -8 % 4 is 0), so compare with == 0, never == 1.

Truthiness: zero is false

C treats zero as false and every other value as true, and comparisons produce the int 1 or 0.

int r = 12 % 5;          /* 2 */
if (r)  printf("A\n");   /* runs: 2 is non-zero */
if (!r) printf("B\n");   /* skipped */

That makes if (x % k) mean "if x is not a multiple of k", the opposite of how it reads. Write the == 0 out. It is the same trap as strcmp from the Strings lesson, which returns 0 for equal strings.

= versus ==

if (x = 5)  { ... }   /* assigns 5, tests 5: always true */
if (x == 5) { ... }   /* compares */

gcc -Wall warns on the first one. if (x % 2 = 0) does not even compile, because you cannot assign to the result of %.

Separate ifs are not a chain

int t = 30;
if (t > 10) { printf("warm\n"); }
if (t > 25) { printf("hot\n"); }

This prints two lines, warm and hot, because each if is tested on its own. Only else if makes the branches exclusive.

switch, && and ||

switch (value) jumps to the matching case constant (integers, chars, enums, never strings) and keeps running into the next case until a break. && and || short-circuit: in n != 0 && 100 / n > 5 the division never runs when n is 0. && binds tighter than ||, so add parentheses when you mix them.

Common mistakes

  • General case first. Testing a single divisor before the combined case prints Fizz for 30 instead of FizzBuzz: the first true branch wins.
  • No final else. A number that matches no test, like 11, prints nothing and the output is empty.
  • Separate ifs instead of else if. 30 prints FizzBuzz, then Fizz, then Buzz on three lines.
  • Bare truthiness. if (n % 3) is true for 7, so 7 prints Fizz. Also remember 0 is a multiple of every number, so 0 prints FizzBuzz.

Your exercise

Write FizzBuzz for one number. The starter already reads n; you write the chain that prints exactly one line. The most likely mistake is ordering: think about which of the four cases is the narrowest.

Up nextLoopsControl Flow

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…