Computer ScienceCore18 min read

Decision Constructs

if, else and switch — and the semicolon that silently breaks everything

This topic appears in:

01

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.

if (marks >= 50) {printf("Pass");} else if (marks >= 40) {printf("Borderline");} else {printf("Fail");}conditions are tested in order and the chain stops at the first that is true

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.

02

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.

03

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.

switch (grade) {case 'A': printf("Excellent"); break;case 'B': printf("Good");break;case 'C':case 'D': printf("Pass");break;/* deliberate fall-through */default:printf("Invalid");}break leaves the switch; without it execution falls into the next case
Worked example

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.

  1. 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.
  2. Months 1, 3, 5, 7, 8, 10 and 12 all have 31 days.Seven values, one outcome.
  3. 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.
  4. Months 4, 6, 9 and 11 stack similarly for 30 days, and case 2 handles February separately.
  5. 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.

04

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

  1. Any non-zero value is true in C; comparisons produce 1 or 0.
  2. Always use braces. if (x > 0); with a stray semicolon has an empty body.
  3. = assigns and compiles; == compares. This is the classic C bug.
  4. switch needs constant integer or character values, and every case needs break.
  5. Short-circuit evaluation means the right-hand side of && may never run — and can be relied on.
05

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.

Worked example

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Practice questions

6 questions · 20 marks · full working on every one

Try each one on paper first, then open the working. The marks are shown where they are actually awarded, because that is where they are actually lost.

Short questions

3 · 6 marks

Two marks each, in the style of the short-question section of the paper. Answer in two or three lines.

SQ1[2 marks]
What values does C treat as true and false in a condition?
Model answer

Zero is false and any non-zero value is true. A comparison such as x > 5 produces 1 when true and 0 when false, but any non-zero number — including a negative one — is accepted as true.

Examiner tip. Mentioning that negatives count as true shows you understand it is "non-zero", not "positive".

SQ2[2 marks]
What does if (x > 0); followed by a block do, and why?
Model answer

The semicolon forms an empty statement which becomes the entire body of the if. The block that follows is then an ordinary block, unattached to the if, and executes every time regardless of x.

Examiner tip. The phrase "empty statement" is what the mark scheme looks for. The block running unconditionally is the consequence.

SQ3[2 marks]
Why must each case in a switch normally end with break?
Model answer

Without it, execution falls through into the following case and continues until a break or the closing brace is reached, so several cases run when only one was intended.

Examiner tip. Add that fall-through is occasionally deliberate, for stacking cases that share an outcome. It shows you know it is a feature being misused rather than a defect.

Solved numericals

2 · 8 marks

Full working, one step per line, with the marks shown where they are awarded.

N1[4 marks]
Write a C fragment that reads a mark and prints A for 80 and above, B for 60–79, C for 40–59 and F below 40.
Full working
  1. if (marks >= 80) printf("A");the most restrictive condition first[1]
  2. else if (marks >= 60) printf("B");no need to test < 80 — reaching here means the first test failed[1]
  3. else if (marks >= 40) printf("C");[1]
  4. else printf("F");, with braces used throughout[1]

A descending chain of else-ifs from 80 down, with a final else.

Examiner tip. Order matters: testing >= 40 first would give C to a mark of 95, because the chain stops at the first true condition.

N2[4 marks]
Explain what is wrong with each: (i) if (x = 10) { … } (ii) a switch case with no break.
Full working
  1. (i) = assigns rather than compares, so x is set to 10[1]
  2. The result, 10, is non-zero and therefore true, so the block always executes and the original value of x is lostboth consequences[1]
  3. (ii) Execution falls through into the next case[1]
  4. and continues through subsequent cases until a break or the closing brace, so multiple outputs appear where one was expected[1]

(i) assignment instead of comparison, always true, value destroyed (ii) fall-through runs the following cases too.

Examiner tip. For (i), note that C compiles it without complaint. That silence is what makes it the most notorious bug in the language.

Long questions

1 · 6 marks

Theory and numerical together, as they appear in the long-question section.

LQ1[6 marks]
A program must classify a character as a vowel, a consonant, a digit, or something else.
  1. State whether a switch or an if-else chain is more suitable, with a reason.
  2. Write the vowel test using a switch, showing the use of fall-through.
  3. Explain why the digit test cannot use the same switch approach conveniently.
Mark scheme
  1. A switch suits the vowel test, since it compares one character variable against a small set of fixed constant values[1]
  2. An if-else chain would need five separate == comparisons joined with ||, which reads worse[1]
  3. switch (ch) { case 'a': case 'e': case 'i': case 'o': case 'u':the empty cases stack[1]
  4. printf("Vowel"); break; default: printf("Not a vowel"); }one statement serves all five vowels[1]
  5. A digit is a range — anything from '0' to '9' — and switch can only match individual constant values[1]
  6. Ten separate cases would be needed, so if (ch >= '0' && ch <= '9') is clearerthe range test is the point[1]

(a) switch, for a fixed set of values (b) five stacked cases sharing one statement (c) a digit is a range, and switch cannot express ranges

Examiner tip. The rule for choosing: switch for a set of discrete values, if-else for a range or any condition more complex than equality.