C / BRANCHING AND SWITCH
if, else if and structuring exclusive branches
Build if / else if / else chains where exactly one branch runs, order conditions so none are unreachable, and know when a final else is required.
What you will learn
- Chain overlapping tests with else if so exactly one body runs per decision
- Read each else if as carrying the negation of every condition above it
- Order branches narrowest first so no branch becomes unreachable dead code
- Add a final else whenever later code reads a value the branches assign
Understanding if, else if and structuring exclusive branches
An if / else if / else chain is one statement, not several. C's grammar knows only `if (expr) statement` and `if (expr) statement else statement`; `else if` is simply an `else` whose statement happens to be another `if`, so a chain of five branches is really five right-nested ifs written flat. That is why control flow enters at the top, tests conditions in order, and leaves the whole construct as soon as one condition is true: the remaining tests live inside the `else` that was just skipped.
The practical consequence is that every condition after the first silently inherits the negation of all the ones above it. Once `score >= 90` has failed, `else if (score >= 80)` already means "between 80 and 89", so restating `score < 90 && score >= 80` is redundant. Because those implied negations accumulate top to bottom, the order of the branches is part of the logic rather than a matter of taste: swapping two branches changes what both of them mean, and putting a wide condition above a narrow one makes the narrow one unreachable.
The last thing to decide is whether the chain is total. A chain that ends with `else if` does nothing at all when no condition matches, which is fine when each body is an optional side effect and wrong when later code reads a variable the branches were supposed to set. Use a single chain when all the branches answer one question about one quantity; when the tests are genuinely independent questions, separate `if` statements are correct precisely because more than one of them may fire.
<stdio.h>
int main(void)
{
int scores[] = {95, 90, 89, 70, 42};
int n = 5;
for (int i = 0; i < n; i++) {
int score = scores[i];
char grade;
if (score >= 90)
grade = 'A';
else if (score >= 80)
grade = 'B';
else if (score >= 70)
grade = 'C';
else
grade = 'F';
printf("score %2d -> grade %c\n", score, grade);
}
return 0;
}
An if / else if / else chain is a single decision: conditions are tested top to bottom, the first true one wins, and every later condition implicitly carries the negation of all the ones above it.
Worked examples
A chain versus three separate ifs
The same three tests give a different answer depending on whether they are joined by else.
<stdio.h>
int main(void)
{
int score = 95;
char chained, separate;
if (score >= 90) chained = 'A';
else if (score >= 80) chained = 'B';
else if (score >= 70) chained = 'C';
else chained = 'F';
separate = 'F';
if (score >= 90) separate = 'A';
if (score >= 80) separate = 'B';
if (score >= 70) separate = 'C';
printf("chained: %c\n", chained);
printf("separate: %c\n", separate);
return 0;
}
Example explained
Line 1`if (score >= 90) chained = 'A';` is true, and its `else` skips everything below it, so the 'B' and 'C' tests are never evaluated.
Line 2The three plain `if` statements are three unrelated decisions, and 95 satisfies all three, so `separate` is written three times.
Line 3Each write overwrites the previous one, so the widest test (`>= 70`) decides the result instead of the narrowest.
Line 4`separate = 'F';` before the tests is doing the job that the final `else` does in the chain.
A branch that can never run
Putting the widest condition first turns a later else if into dead code that still compiles cleanly.
<stdio.h>
int main(void)
{
for (int t = -5; t <= 35; t += 20) {
const char *label;
/* widest test first: the next branch is unreachable */
if (t < 30)
label = "not hot";
else if (t < 10)
label = "cold";
else
label = "hot";
printf("%3d C: %s\n", t, label);
}
return 0;
}
Example explained
Line 1`if (t < 30)` is true for -5, so the chain stops there and labels a freezing temperature "not hot".
Line 2Reaching `else if (t < 10)` requires `t >= 30`, and no value is both, so that branch can never execute.
Line 3The compiler accepts this because each condition is valid on its own; nothing checks that a branch is reachable.
Line 4Testing `t < 10` first and `t < 30` second makes each branch cover only the gap left by the ones above it.
A chain with no final else
When no condition matches, the whole statement does nothing and a stale value gets used.
<stdio.h>
int main(void)
{
int codes[] = {200, 404, 302, 500};
const char *kind = "unknown";
for (int i = 0; i < 4; i++) {
int c = codes[i];
if (c >= 200 && c < 300)
kind = "success";
else if (c >= 400 && c < 500)
kind = "client error";
/* nothing for 3xx or 5xx, and no final else */
printf("%d -> %s\n", c, kind);
}
return 0;
}
Example explained
Line 1For 302 and 500 neither condition holds, so the entire `if` statement executes nothing.
Line 2`kind` is declared outside the loop, so it still holds "client error" from the 404 iteration and that stale value is printed.
Line 3If `kind` were declared inside the loop with no initializer, printing it would read an indeterminate value, which is undefined behavior rather than a merely wrong label.
Line 4Adding `else kind = "other";` makes the chain total, so every input leaves `kind` freshly assigned.
Important notes
`else if` is not a keyword; it is an `else` whose statement is another `if`. That is also why an `else` always pairs with the nearest unmatched `if`, which matters as soon as you drop the braces.
Only one body executes, but every condition down to the winning one is evaluated, so conditions that call functions or modify variables do that work on the way down the chain.
Common mistakes
Writing consecutive plain if statements for overlapping tests: several bodies run and the last assignment wins, so a score of 95 comes out as 'C'.
Listing the loosest condition first, for example `if (t < 30)` before `else if (t < 10)`: the second branch is unreachable, and the compiler gives no warning because both conditions are individually legal.
Ending the chain at the last else if and then using a variable the branches were meant to set: you get the previous iteration's value, or undefined behavior if the variable was never initialized.
Try it yourself
Change, predict, then run
Write a chain that turns a battery percentage into a label: "full" at exactly 100, "high" for 80-99, "medium" for 30-79, "low" below 30, and "invalid" outside 0-100. Run it on 100, 99, 80, 30, 29, -1 and confirm each boundary value reaches the branch you meant.
Open the C workspaceCheck your understanding
A chain tests `if (n % 2 == 0)` and prints "even", then `else if (n % 3 == 0)` prints "divisible by 3", then an `else` prints "other". For n = 6 the program prints only "even". Why?
- Once a condition is true, the matching `else` skips the rest of the chain, so `n % 3 == 0` is never evaluated at all
- All conditions are evaluated, but only the body of the last matching branch is kept
- `%` binds more tightly than `==`, so the second condition parses into something that is always false
- 6 % 3 evaluates to 0, and a condition whose value is 0 counts as false
Show answer
The second test lives inside the `else` of the first, and that `else` is only entered when `n % 2 == 0` is false, so for n = 6 the divisibility-by-3 test is never reached. Option 2 is tempting because that overwrite behavior is what you see with three independent `if` statements assigning to one variable, but in that case every matching body actually runs, whereas a chain runs at most one. Option 4 confuses the remainder with the comparison: `6 % 3` is 0, so `6 % 3 == 0` is true, and the chain would have printed it had it been reached.