Module I · Decision and Case Control · Lecture 13
🎯 Why This Matters
Every decision your program will ever make comes down to one basic tool: the if statement. But if on its own has real limits — and understanding exactly where those limits show up is what makes if-else, the if-else-if ladder, and nested if statements actually make sense, instead of feeling like arbitrary extra syntax to memorize. This post builds each one up from the last, showing not just how each works, but why it exists.
1️⃣ Starting With Just if
An if statement runs a block of code only when its condition is true. On its own, it has no memory of “otherwise” — if the condition is false, C simply moves to whatever comes next in the program.
Example 1: Checking a Positive Number
#include <stdio.h>
int main() {
int n = 12;
if (n > 0) {
printf("The number is positive.\n");
}
printf("Done checking.\n");
return 0;
}
🖥️ Output:
The number is positive.
Done checking.
Example 2: Checking Even Numbers
#include <stdio.h>
int main() {
int n = 7;
if (n % 2 == 0) {
printf("The number is even.\n");
}
printf("Done checking.\n");
return 0;
}
🖥️ Output:
Done checking.
Notice this second example — with n = 7, the condition is false, so the if block never runs, and the program silently skips straight to "Done checking." with no message about the number at all. That silence is the seed of the problem we’re about to run into.
2️⃣ The Disadvantage: Using Only if for Multiple Outcomes
What happens when you need to check several possible outcomes for the same value — say, whether a number is positive, negative, or zero? Using only if, you might be tempted to write three separate, independent checks:
#include <stdio.h>
int main() {
int n = 15;
if (n > 0) {
printf("Positive\n");
}
if (n < 0) {
printf("Negative\n");
}
if (n == 0) {
printf("Zero\n");
}
return 0;
}
🖥️ Output:
Positive
This actually works — but look closely at how it works. C doesn’t know these three conditions are related to each other. It checks n > 0 (true, prints “Positive”), and then it keeps going — it still checks n < 0, and it still checks n == 0, even though we already know the answer by then. Three separate if statements mean three separate, unrelated checks, every single time, no matter how early the real answer was already found.
A Second Example — Where This Becomes a Real Bug
#include <stdio.h>
int main() {
int marks = 95;
if (marks >= 90) {
printf("Grade: A\n");
}
if (marks >= 75) {
printf("Grade: B\n");
}
if (marks >= 50) {
printf("Grade: C\n");
}
return 0;
}
🖥️ Output:
Grade: A
Grade: B
Grade: C
This is the real disadvantage, not just a performance footnote: a student who scored 95 gets told they earned three different grades at once. Every condition here is independently true for a high score, so every single block runs. With separate if statements, C has no built-in concept of “these outcomes are mutually exclusive — stop after the first match.” That has to come from somewhere else.
⚠️ The core problem: Independent if statements are checked independently — always. They’re the right tool when several unrelated things might all need to happen. They’re the wrong tool the moment you need exactly one outcome out of several possibilities.
3️⃣ if-else: Guaranteeing Exactly One Branch Runs
else attaches a second block to an if, and C guarantees that exactly one of the two ever runs — never both, never neither. This directly solves the problem above, at least for two-way decisions.
Example 1: Positive or Not (Fixed)
#include <stdio.h>
int main() {
int n = -4;
if (n > 0) {
printf("Positive\n");
} else {
printf("Not positive\n");
}
return 0;
}
🖥️ Output:
Not positive
Unlike Example 2 back in Section 1, this version never goes silent — every possible value of n produces exactly one message. There’s no gap left uncovered, because else means “everything the if didn’t catch,” automatically.
Example 2: Even or Odd
#include <stdio.h>
int main() {
int n = 7;
if (n % 2 == 0) {
printf("Even\n");
} else {
printf("Odd\n");
}
return 0;
}
🖥️ Output:
Odd
Example 3: Why It’s Better — Same Grade Bug, Now Fixed for Two Outcomes
#include <stdio.h>
int main() {
int marks = 95;
if (marks >= 50) {
printf("Pass\n");
} else {
printf("Fail\n");
}
return 0;
}
🖥️ Output:
Pass
Only one message prints, guaranteed — because else doesn’t need its own condition. It simply means “if the above wasn’t true.” The moment C finds one branch is true, it never even evaluates the other — unlike the three separate if statements from Section 2, which always checked every single condition regardless of what was already found.
But notice this only cleanly solves a two-way decision. Our three-grade example (A/B/C) needs more than “true” or “false” — it needs several outcomes in sequence. That’s the next problem to solve.
4️⃣ if-else-if: Handling More Than Two Outcomes
Chaining else if after the first if lets you test several conditions in sequence. C checks them top to bottom and stops at the first one that’s true — every condition after that is skipped automatically, which is exactly the behavior the multiple-if version in Section 2 was missing.
Example 1: The Grade Problem, Finally Fixed
#include <stdio.h>
int main() {
int marks = 95;
if (marks >= 90) {
printf("Grade: A\n");
} else if (marks >= 75) {
printf("Grade: B\n");
} else if (marks >= 50) {
printf("Grade: C\n");
} else {
printf("Grade: F\n");
}
return 0;
}
🖥️ Output:
Grade: A
Only one grade prints this time. C checks marks >= 90, finds it true, prints "Grade: A", and — crucially — never even looks at the remaining conditions, even though marks >= 75 and marks >= 50 are also technically true for a 95. This is the direct fix for the exact bug from Section 2.
Example 2: Positive, Negative, or Zero (Three-Way, Cleanly)
#include <stdio.h>
int main() {
int n = 0;
if (n > 0) {
printf("Positive\n");
} else if (n < 0) {
printf("Negative\n");
} else {
printf("Zero\n");
}
return 0;
}
🖥️ Output:
Zero
Compare this directly to Section 2’s very first example, which used three separate if statements for this same problem. That version happened to work for n = 15, but it worked by accident — nothing stopped multiple messages from printing if the conditions had overlapped. This version can never do that, by construction.
Example 3: Eligibility Bands (BMI Classifier)
#include <stdio.h>
int main() {
float bmi = 22.4;
if (bmi < 18.5) {
printf("Underweight\n");
} else if (bmi < 25.0) {
printf("Normal\n");
} else if (bmi < 30.0) {
printf("Overweight\n");
} else {
printf("Obese\n");
}
return 0;
}
🖥️ Output:
Normal
💡 Order matters in a ladder: Each condition only needs to rule out the ranges already eliminated by the checks above it — that’s why bmi < 25.0 is safe here without also writing bmi >= 18.5; by the time C reaches that line, it already knows the first condition was false.
5️⃣ Nested if: Decisions Inside Decisions
An if-else-if ladder is great when you’re testing one variable against a sequence of thresholds. But sometimes a decision genuinely depends on more than one independent question, where the second question only even makes sense to ask after the first has been answered. That’s what nesting — placing one complete if statement inside another — is for.
Example 1: Largest of Three Numbers
#include <stdio.h>
int main() {
int a = 15, b = 42, c = 8;
if (a > b) {
if (a > c) {
printf("Largest: %d\n", a);
} else {
printf("Largest: %d\n", c);
}
} else {
if (b > c) {
printf("Largest: %d\n", b);
} else {
printf("Largest: %d\n", c);
}
}
return 0;
}
🖥️ Output:
Largest: 42
The inner if (a > c) only ever runs once we already know a > b is true — it would be a wasted, meaningless comparison otherwise. This is the real advantage of nesting: the inner condition is only evaluated when the outer condition has already narrowed things down, so every comparison that actually runs is doing useful work, not blindly re-checking things a ladder would’ve had to re-ask from scratch.
Example 2: Login Check With a Nested Condition
#include <stdio.h>
#include <string.h>
int main() {
char username[20] = "admin";
char enteredUser[20] = "admin";
int enteredPass = 1234;
int correctPass = 1234;
if (strcmp(enteredUser, username) == 0) {
if (enteredPass == correctPass) {
printf("Login successful\n");
} else {
printf("Incorrect password\n");
}
} else {
printf("Unknown username\n");
}
return 0;
}
🖥️ Output:
Login successful
Notice the password is only ever checked if the username was already correct — exactly the kind of dependency a flat if-else-if ladder can’t naturally express, since a ladder tests one variable against a sequence of values, not “check B, but only if A already passed.”
Example 3: Voting Eligibility (Two Independent Conditions, Nested)
#include <stdio.h>
int main() {
int age = 20;
char citizen = 'y';
if (age >= 18) {
if (citizen == 'y') {
printf("Eligible to vote\n");
} else {
printf("Not a citizen — not eligible\n");
}
} else {
printf("Too young — not eligible\n");
}
return 0;
}
🖥️ Output:
Eligible to vote
This could also have been written as a single condition using && (if (age >= 18 && citizen == 'y')), and often should be, for something this simple. But nesting it like this has one real advantage: each failure gets its own specific message — “too young” versus “not a citizen” — something a single combined condition can’t distinguish on its own.
6️⃣ Curly Braces: Where a Block Actually Begins and Ends
Every example so far has used { } around each block. This isn’t just a style habit — it’s how C knows exactly which statements belong to the if and which don’t.
Without Braces: Only One Statement Belongs to the if
#include <stdio.h>
int main() {
int n = 7;
if (n % 2 == 0)
printf("Even\n");
else
printf("Odd\n");
return 0;
}
🖥️ Output:
Odd
This works fine — when a block is a single statement, braces are technically optional. C simply attaches the one line immediately following if (or else) to that branch.
Where Leaving Them Out Goes Wrong
#include <stdio.h>
int main() {
int n = -5;
if (n > 0)
printf("Positive\n");
printf("Number was checked.\n"); // NOT part of the if!
return 0;
}
🖥️ Output:
Number was checked.
Even though n is negative, "Number was checked." still prints. Indentation made it look like both lines belonged to the if, but C only ever attaches the single statement immediately after the condition — the second printf is really just an ordinary, unconditional line that happens to run every time, indentation or not.
The Fix: Always Brace Multi-Statement Blocks
#include <stdio.h>
int main() {
int n = -5;
if (n > 0) {
printf("Positive\n");
printf("Number was checked.\n");
} else {
printf("Not positive.\n");
printf("Number was checked.\n");
}
return 0;
}
🖥️ Output:
Not positive.
Number was checked.
{ } groups any number of statements into a single block, so C treats them as one unit belonging entirely to the if or else. The moment a branch needs more than one line, braces stop being optional in any practical sense — leaving them off is exactly how the bug above happens, silently, with no compiler warning at all.
💡 A habit worth building now: Many experienced C programmers use { } even for single-statement blocks, every time, without exception. It costs nothing, and it means that if someone later adds a second line to that block, they can’t accidentally recreate the bug above just by forgetting to add braces at the same time.
📊 The Full Progression, Side by Side
| Tool | Good for | Limitation it solves |
|---|---|---|
Plain if | A single optional action | — |
Multiple separate ifs | Several unrelated checks that may all apply | Wrong when outcomes should be mutually exclusive |
if-else | Exactly one of two outcomes | Fixes the “both or neither” gap from separate ifs |
if-else-if ladder | Exactly one of several outcomes, same variable | Extends if-else to more than two possibilities |
Nested if | A decision that only makes sense after another has resolved | Avoids asking a sub-question before it’s relevant |
🎓 Practice Exercises
Exercise 1: Spot the Section-2 Bug
int speed = 85;
if (speed > 60) {
printf("Speeding\n");
}
if (speed > 80) {
printf("Dangerously speeding\n");
}
(Answer: both lines print for speed = 85 — exactly the Section 2 problem. Rewrite using else if so only the more specific “Dangerously speeding” shows.)
Exercise 2: Convert a Ladder Into Nested Ifs
Rewrite the BMI classifier from Section 4 using nested if statements instead of an else if ladder. Is the result easier or harder to read? Why do you think that is?
Exercise 3: Find the Missing Brace Bug
int score = 40;
if (score >= 50)
printf("Pass\n");
printf("Result recorded.\n");
else
printf("Fail\n");
(Answer: this one won’t even compile — the un-braced if only “claims” the first printf, so printf("Result recorded.\n"); is a separate statement, and the compiler then finds a stray else with no matching if attached to it. Adding braces around both printf lines under the if fixes it.)
❓ Frequently Asked Questions
Q: If else-if ladders fix the multiple-if problem, why would I ever use several separate if statements?
When the outcomes genuinely aren’t mutually exclusive — for example, checking a shopping cart for “is it empty,” “does it qualify for free shipping,” and “does it have an out-of-stock item” all at once. Those can all be true simultaneously, so separate ifs are correct there; the problem in Section 2 was specifically using separate ifs for outcomes that were supposed to be mutually exclusive.
Q: Is there a limit to how many else-if steps I can chain, or how deep I can nest?
No hard limit in the language, but both become hard to read fast. A ladder longer than about 5–6 steps, or nesting beyond 2–3 levels deep, is usually a sign the logic could be reorganized — sometimes with a switch statement (a topic for the next post), sometimes by breaking the checks into smaller named conditions first.
Q: Can I mix nested ifs and else-if ladders in the same program?
Yes, constantly — real programs do this all the time. A ladder handles the main categories, and a nested if inside one branch handles a finer distinction only relevant to that specific case, exactly like the login example that nested a password check inside a username check.
✅ Key Takeaways
- A plain
ifis fine for a single optional action, but using several independentifstatements for outcomes that should be mutually exclusive lets more than one block run at once — a real, silent bug. if-elseguarantees exactly one of two branches runs, becauseelseneeds no condition of its own — it simply means “otherwise.”- An
if-else-ifladder extends that same guarantee to more than two outcomes, checking conditions in order and stopping at the first match. - Nested if statements let a sub-decision run only once an outer decision has already been resolved — useful exactly when one question only makes sense after another has been answered.
- Curly braces define exactly which statements belong to a branch — leaving them off a multi-statement block doesn’t cause an error, it silently changes what your program actually does.
🚀 Next up: With if, if-else, if-else-if, and nested if all covered, we’ll look at the switch-case statement — a cleaner alternative to a long if-else-if ladder when you’re checking one variable against several exact values.