Module 5 · Decisions
if, else if, else: Choosing a Path
In this lesson
- Write an
if, anif elseand anelse ifladder, with braces around every body. - Trace a ladder with a table of inputs and the path each input takes.
- Explain why a ladder's order decides its answers, and why one
=in a condition is a bug.
In Module 4, Amara wrote the leap year rule as one expression, and it printed 1 or 0. Her calendar app wanted a sentence instead: "2024 has 366 days".
A 1 on the screen is not a sentence. To print one of two sentences, the program has to choose which line runs.
Every program so far ran every line, top to bottom, every time. Today the road forks.
if: a block that runs only when the answer is not zero
An if statement asks a question. It runs a block of code only when the answer is true. In C, "true" means "not zero", the Module 2 rule.
The if statement
if (condition) {
statements
}
ifis the keyword, and brackets always follow it.conditionis any expression. Zero means false; every other value means true.- The braces hold the body: the lines that run only when the condition is true.
- After the closing brace the program carries on, whichever way the question went.
#include <stdio.h>
int main(void)
{
int stock = 7;
if (stock < 10) {
printf("reorder\n");
}
printf("stock checked\n");
return 0;
}
reorder
stock checked
Change the 7 to 12 and only the second line prints. The printf inside the braces is skipped; the one after them is not.
So an if does not end the program or choose an exit. It decides whether one block runs, and then the program continues.
else: exactly one of two paths
An else gives the false answer a block of its own. Exactly one of the two blocks runs: never both, and never neither.
#include <stdio.h>
int main(void)
{
int year = 2024;
int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
if (leap) {
printf("%d has 366 days\n", year);
} else {
printf("%d has 365 days\n", year);
}
return 0;
}
2024 has 366 days
The expression is Amara's from Module 4, unchanged. What is new is that its 1 or 0 now decides which printf runs.
So the leap year rule did not change at all. It stopped being an answer to print and became an answer to act on.
The else if ladder: the first true condition wins
Most real questions have more than two answers. C chains them with else if, and the chain is usually called a ladder.
The else if ladder
if (condition_1) {
runs when condition_1 is true
} else if (condition_2) {
runs when condition_1 was false and condition_2 is true
} else {
runs when every condition above was false
}
- C tests the conditions from the top, one at a time.
- The first true one runs its block, and every test below it is skipped.
- The last
elseis optional. Without it, a value that matches nothing runs nothing.
#include <stdio.h>
int main(void)
{
int marks = 72;
if (marks >= 80) {
printf("distinction\n");
} else if (marks >= 60) {
printf("merit\n");
} else if (marks >= 40) {
printf("pass\n");
} else {
printf("fail\n");
}
return 0;
}
merit
A trace table is the tool for reading a ladder. One row per input, and a column for every test C actually evaluated.
| marks | Tests evaluated, in order | Path taken |
|---|---|---|
| 95 | >= 80 true | distinction |
| 72 | >= 80 false, >= 60 true | merit |
| 60 | >= 80 false, >= 60 true | merit |
| 45 | >= 80 false, >= 60 false, >= 40 true | pass |
| 12 | all three false | fail, the else |
Read the middle column for 72. The test >= 40 never ran. The ladder stopped at the first true answer.
Now the ordering rule. Bob, who always rushes, writes the same ladder starting from the easiest test.
#include <stdio.h>
int main(void)
{
int marks = 95;
if (marks >= 40) {
printf("pass\n");
} else if (marks >= 60) {
printf("merit\n");
} else if (marks >= 80) {
printf("distinction\n");
} else {
printf("fail\n");
}
return 0;
}
pass
Trace 95 through it. The first test, >= 40, is true, so the ladder stops there. The same happens for 62 and for 100.
Every mark from 40 up passes the first test, so the merit and distinction blocks can never run. The compiler says nothing; it cannot know what Bob meant.
So the rule for any ladder is this. When one condition's range contains another's, the smaller range must come first. Here, "80 and above" sits inside "40 and above", so it goes higher up.
Any int is a condition, and 0 is false
The brackets after if do not need a comparison. Any expression with a number works, and only zero means no.
#include <stdio.h>
int main(void)
{
int stock = 0;
int n = -7;
if (stock) {
printf("in stock\n");
} else {
printf("sold out\n");
}
if (n % 2) {
printf("%d is odd\n", n);
}
return 0;
}
sold out
-7 is odd
if (stock) means exactly if (stock != 0). The short form reads well for a count that answers "is there any".
The second test is subtler. -7 % 2 is -1, the Module 4 sign rule, and -1 is not zero. So the test is true for negative odd numbers too, where n % 2 == 1 would have been false.
So a condition is not a special kind of value. It is a number, and only zero says no.
One equals sign: the bug that compiles
= stores a value. == asks a question. Inside a condition, the first one is almost always a typing slip.
#include <stdio.h>
int main(void)
{
int marks = 35;
if (marks = 100) {
printf("full marks\n");
}
printf("marks is now %d\n", marks);
return 0;
}
full marks
marks is now 100
Two things went wrong at once. The assignment's value is 100, which is not zero, so the test is always true. And the student's real mark has been overwritten.
The Playground compiles this in silence, because it has no -Wall. A local gcc -Wall on GCC 12 says warning: suggest parentheses around assignment used as truth value [-Wparentheses].
So when an if is true for every input you try, look for a lone = first.
Braces on every body, and the bug that taught everyone why
C allows an if without braces when its body is a single statement. This track never uses that permission, and here is the reason.
In February 2014, Apple shipped a fix for its TLS code on iOS and macOS, logged as CVE-2014-1266. A function that verified a server's signature contained two identical goto fail; lines under an if with no braces. Only the first line belonged to the if. The second ran every time, jumped past the real check, and returned success. People called it "goto fail".
You can build the same trap with nothing but printf.
#include <stdio.h>
int main(void)
{
int marks = 35;
if (marks >= 40)
printf("pass\n");
printf("certificate printed\n");
return 0;
}
certificate printed
The indentation says both lines belong to the if. The compiler ignores indentation, so only the first one does.
The Playground is silent again. A local gcc -Wall says warning: this 'if' clause does not guard... [-Wmisleading-indentation]. With braces, the question never arises.
So braces are not decoration. They are the only thing that tells C where a body ends.
One condition, one body, and nothing at all when the answer is no.
#include <stdio.h>
int main(void)
{
int celsius = 0;
scanf("%d", &celsius);
if (celsius < 0) {
printf("freezing\n");
}
return 0;
}
freezing
That output is for the input -3. For 5 the program prints nothing, which is exactly what it was asked to do.
Two paths, and the Module 4 remainder rule decides which one a negative number takes.
#include <stdio.h>
int main(void)
{
int n = 0;
scanf("%d", &n);
if (n % 2 == 0) {
printf("%d is even\n", n);
} else {
printf("%d is odd\n", n);
}
return 0;
}
-7 is odd
That output is for the input -7. Comparing against 0 works for both signs, because a remainder of -1 is still not 0.
A cinema ticket by age. A guard throws out impossible input first, then the bands run from the youngest up.
#include <stdio.h>
int main(void)
{
int age = 0;
scanf("%d", &age);
if (age < 0) {
printf("invalid age\n");
} else if (age < 3) {
printf("ticket 0\n");
} else if (age < 12) {
printf("ticket 150\n");
} else if (age >= 60) {
printf("ticket 200\n");
} else {
printf("ticket 300\n");
}
return 0;
}
ticket 200
That output is for the input 65. Here the tests use <, so "under 3" sits inside "under 12" and comes first. The rule is the same one, read from the other end.
Where this is used
- Exam results. Bangladesh's education boards publish a grade table: 80 and above is A+, down to F below 33. Every program that prints it is an
else ifladder, strictest band first. - Thermostats. The Linux kernel's
step_wisethermal governor compares a temperature against trip points. A chain ofifstatements decides whether cooling goes up, down or stays. - Input validation. The nginx web server refuses a request body larger than
client_max_body_sizewith status 413. One comparison, one branch, before any real work starts. - Login checks. OpenSSH's server counts failed password attempts and disconnects after
MaxAuthTriesof them. That is anifon a counter.
Common mistakes
1. One equals sign in the condition.
if (marks = 100) {
printf("full marks\n");
}
Silent on the Playground. A local gcc -Wall says warning: suggest parentheses around assignment used as truth value [-Wparentheses]. The test is always true and marks is overwritten. Write ==. You will make this one when you type fast, because both keys are the same key.
2. A semicolon straight after the condition.
if (marks >= 40);
{
printf("pass\n");
}
Silent on the Playground. A local gcc -Wall reports this 'if' clause does not guard... [-Wmisleading-indentation], and -Wextra adds suggest braces around empty body in an 'if' statement [-Wempty-body]. The semicolon is an empty body, so "pass" prints for every mark. Delete it; an if line never ends in a semicolon.
3. Writing the symbols in the order you say them.
if (marks =< 40) {
printf("fail\n");
}
An error on every command line, the Playground included: error: expected expression before '<' token. The operator is <=, with the angle first. You say "less than or equal", and the symbols follow the words.
4. The widest test at the top.
if (marks >= 40) {
printf("pass\n");
} else if (marks >= 80) {
printf("distinction\n");
}
No message at any command line. The second block can never run, because every mark that reaches 80 already passed the first test. Put the smaller range first, and trace one input from each band before you trust it.
Alice prints report cards with the Bangladesh board table. 80 to 100 is A+, 70 to 79 A, 60 to 69 A-, and 50 to 59 B. Then 40 to 49 is C, 33 to 39 D, and 0 to 32 F.
Input. One line with one integer marks.
Output. One line with the grade, or invalid if marks is below 0 or above 100.
Constraints. -1000 <= marks <= 1000.
Sample. Input 79 gives A. Input 101 gives invalid.
#include <stdio.h>
int main(void)
{
int marks = 0;
scanf("%d", &marks);
/* One guard for the impossible marks, then one rung per band. */
return 0;
}
Graded as grade-bands. Every boundary in the table is a hidden test, on both sides.
Kenji ran three laps, and the leaderboard shows only his best score. Two laps can tie.
Input. One line with three integers.
Output. One line with the largest of the three.
Constraints. Each value is between -1000000000 and 1000000000.
Sample. Input 3 9 5 gives 9. Input 7 2 7 gives 7.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
/* Two separate ifs are enough. Equal values must not break it. */
return 0;
}
Graded as largest-of-three. The largest value moves through all three positions in the hidden tests.
Amara's calendar needs a plain answer for any year: is it a leap year or not?
Input. One line with one integer y.
Output. One line: yes if y is a leap year, otherwise no.
Constraints. 1 <= y <= 9999.
Sample. Input 1900 gives no. Input 2000 gives yes.
#include <stdio.h>
int main(void)
{
int y = 0;
scanf("%d", &y);
/* The Module 4 expression decides; an if else prints the word. */
return 0;
}
Not graded on its own. The problems lesson's date-check uses this rule inside a bigger question.
Zara tests a geometry app with three lengths. Each length must be shorter than the other two added together, or there is no triangle. Equal to the sum is flat, and does not count.
Input. One line with three integers a, b and c.
Output. One line: not a triangle, or else equilateral, isosceles or scalene.
Constraints. 1 <= a, b, c <= 1000000000.
Sample. Input 2 2 3 gives isosceles. Input 1 2 3 gives not a triangle.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
/* Is it a triangle at all? Then the narrowest kind first. */
return 0;
}
Graded as triangle-kind. The order of the rungs is the whole difficulty.
Common doubts
Do I really need braces when the body is one line?
C does not require them. This track does, because the next line somebody adds will look guarded and will not be. Two characters are a cheap price for that.
Is
else ifa keyword of its own?No. It is an
elsewhose body happens to be anotherif. Writing both words on one line is a layout habit, and every C programmer reads the ladder that way.Why a ladder, and not several separate
ifstatements?Separate statements are all tested, so more than one block can run. For marks of 95, three separate tests would print distinction, merit and pass. A ladder stops at the first true test, so exactly one block runs.
Is
if (x)exactly the same asif (x != 0)?Yes, to the compiler. Use the short form for a flag or a count that answers "is there any". Use the long form when the zero is a real value you are comparing against.
Does a long ladder make a program slow?
Not in any way you could measure yet. Lesson 5 shows what GCC actually builds from a condition, and it is often less than the source suggests.
Key takeaways
- An
ifruns its body only when the condition is not zero. - An
elsegives the false answer its own body, and exactly one of the two runs. - A ladder tests from the top and stops at the first true condition.
- A trace table shows every test that ran; when one range contains another, the smaller goes first.
- A lone
=inside a condition assigns, and the Playground stays silent about it. - Every body gets braces, even a body of one line.
Next, an if goes inside another if, and an else loses track of which one it belongs to.
End of lesson 1
Mark it done, and your progress moves with you.
Next: Nested Conditions and the Dangling else