if and else
An if evaluates a condition and executes its block when the condition is non-zero. C has no separate boolean type in its original form: a comparison produces 1 for true and 0 for false, and any non-zero value counts as true.
Braces are optional when the block is a single statement, and omitting them is how a great deal of C goes wrong. Write them always — adding a second statement later then does what you expect rather than silently escaping the if.
Step through if / else. The condition is evaluated once and control jumps straight to the else block — line 3 never runs at all, which is why a trace table has no row for it.
The two mistakes that compile
C accepts both of these without a word, which is what makes them dangerous.
The first is = instead of ==. if (x = 5) assigns 5 to x, then treats the result — 5, which is non-zero — as the condition. The branch always runs and x has been changed. Some programmers write if (5 == x) deliberately, because if (5 = x) would then be a compile error.
The second is a stray semicolon: if (x > 0); ends the if statement immediately with an empty body. The block that follows is then an ordinary block that always runs, whatever x is.
The dangling else
Without braces, an else attaches to the nearest unmatched if, regardless of indentation. Indentation means nothing to the compiler, so a program can be laid out to suggest one structure and compiled as another. Always use braces and the ambiguity cannot arise.
switch
When one variable is tested against several fixed values, a switch is clearer than a chain of else-ifs — and clearer is the only reason to prefer it, since both do the same job.
Its restriction is that the values must be constants of an integer or character type. You cannot switch on a range, on a float, or on a string, which is exactly when a chain of if-else remains the right choice.
A program prints the number of days in a month given its number. Explain why a switch suits this and where fall-through is useful.
- One variable is compared against twelve fixed integer values, which is precisely what switch is for.A chain of twelve else-ifs would work but reads far worse.
- Months 1, 3, 5, 7, 8, 10 and 12 all have 31 days.Seven values, one outcome.
- Stack the cases:
case 1: case 3: case 5: … case 12: printf("31"); break;Deliberate fall-through — each empty case runs on into the next until it reaches the statement. - Months 4, 6, 9 and 11 stack similarly for 30 days, and case 2 handles February separately.
default: printf("Invalid month");A default catches 0, 13 and anything else, and should be present in every switch.
Stacked cases for the 31- and 30-day months, case 2 for February, and a default for invalid input.
Fall-through: usually a bug, occasionally intended
Omitting break makes execution continue into the following case and keep going until it meets a break or the closing brace. Almost always this is a forgotten break, and the symptom is several outputs appearing when one was expected. The exception is stacking cases deliberately, as with C and D above, where two values are meant to do the same thing.
Nesting and combining conditions
An if may contain another, and often a single condition combined with && or || is clearer than nesting two.
C uses short-circuit evaluation: in a && b, if a is false then b is never evaluated, because the answer is already known. This is not merely an optimisation — it is relied on for safety, as in if (count > 0 && total / count > 50), where the division is never attempted when count is zero.
Before you leave this chapter
- Any non-zero value is true in C; comparisons produce 1 or 0.
- Always use braces.
if (x > 0);with a stray semicolon has an empty body. =assigns and compiles;==compares. This is the classic C bug.- switch needs constant integer or character values, and every case needs break.
- Short-circuit evaluation means the right-hand side of && may never run — and can be relied on.
Nested decisions, and keeping them readable
An if may contain another, and sometimes it must: a discount that applies only to members, and then differs by amount, is genuinely two decisions. But nesting deepens quickly, and code indented four levels is hard to follow and easy to get wrong.
Two techniques keep it flat. Combine conditions with && where the tests are independent — if (isMember && total > 5000) replaces two nested ifs. And handle the exceptional cases first and leave, so the main path is not buried inside them.
A shop gives 10% discount to members spending over Rs 5000 and 5% to members spending less. Non-members get nothing. Write it two ways and compare.
- Nested:
if (member) { if (total > 5000) d = 0.10; else d = 0.05; } else d = 0;Correct, and it mirrors the structure of the sentence — membership decided first, then the amount. - Flattened:
if (!member) d = 0; else if (total > 5000) d = 0.10; else d = 0.05;The exceptional case is dealt with first and the rest reads as a simple chain. - Both are correct. The second is shallower and easier to extend.Adding a third membership tier means one more else-if rather than another level of nesting.
- What matters is that every path sets
d.A path that leaves d uninitialised produces a discount of whatever rubbish was in that memory — the classic uninitialised-variable bug.
Both work; the flattened chain is easier to read and to extend, and every path must assign d.