Choosing with case
One selector, several labelled branches: a case statement replaces a long chain of ifs, groups labels together, and never falls through.
A chain of ifs, written once
When every branch tests the same value against a fixed list, case says it better: case code of 1: ...; 2: ...; end;. The values are called labels and each label owns one statement.
The selector has to be an ordered set of values: a whole number, a char, a boolean or an enumerated type. A real is rejected outright, because a case is built on the idea of a next value and a real has none.
A string works in Free Pascal as an extension that compares whole strings, but it is not part of the classic language. A selector that is an array, or a record, is a type error with no extension to rescue it.
The gain is not only brevity. A case states that every branch answers the same question, which a chain of ifs does not: in a chain each test may read a different value, and a reader has to check. The case makes the shared selector explicit.
Grouped labels and the else
Several labels may share one branch: 3, 4: writeln('pending'); writes once for either three or four. A range written 1..5: also works, which is useful for bands of values.
An else after the last label catches everything not listed. Without it, an unmatched selector simply does nothing — no error, no message — which is why an else is usually worth having.
The else in a case has no then, unlike the if form, and the whole statement closes with end; and a semicolon.
One branch, then out
Unlike C, a case does not fall through. One matching branch runs and the statement is over, so there is no break to remember and no accidental running into the next label.
A branch that needs more than one statement gets a begin … end block, exactly as an if branch would. The block is the one statement that branch owns.
Two labels for the same value is a compile error rather than a silent first-match. That is a small kindness: overlapping branches are a bug in the program, not in the compiler.
That difference from C removes a family of bugs. There is no break to forget, so a branch cannot run into the next one, and the block rule is the same as everywhere else in Pascal. The statement closes with end; like any other.
Numbers, groups and an else
program Main;
var
code: integer;
begin
code := 2;
case code of
1: writeln('open');
2, 3: writeln('closed');
else
writeln('unknown');
end;
end.How the compiler chooses a branch
The selector is evaluated once. For a dense list of integer labels the compiler can build a jump table and reach the matching branch in one step; for sparse or ranged labels it emits comparisons. Either way the program does not walk every label in source order, which is why a case is cheaper than a long if chain when the values are a real set.
Labels must be constants known at compile time, because the table is built before the program runs. A range such as 1..5 is a compact way to list five labels, and grouped labels share one branch. If two labels can match the same value, the table has no single answer, so the compiler rejects the overlap instead of choosing one.
A worked case, in words
Take a case over code with a branch for 1, a shared branch for 2 and 3, and an else. With code holding 3, the selector is evaluated once, the labels 2 and 3 share a branch, and that branch prints closed. The statement then ends; no other branch is tried and nothing falls through to the else.
Change code to 7 and the else runs. Change it to 1 and the first branch runs. The else is the branch for every value the labels did not claim, not a default that always runs. Delete it and a value of 7 prints nothing at all.
Three case mistakes to watch for: using a real as the selector, which the compiler rejects; relying on a string selector as though it were standard Pascal, when it is a Free Pascal extension; and letting two label ranges overlap, which is a compile error even when the branches are identical.
Try these next
- Add a branch for codes 5 through 7 and send a code of 6 through it.
- Replace an existing
ifchain with a case and check that every branch still fires. - Delete the
elsefrom a case, send an unmatched value, and confirm that nothing prints.
Case over a code, and over a letter
Run it, then change the first code to 1 and to 7. Then change the letter to one that is not listed and see which branch answers.
A real cannot be a case selector. The compiler asks for an ordinal expression, so band a real with if and comparisons instead of trying to list its values.
Ranges, groups and a block
Run it, then change `command` to `A` and `code` to 5. Predict which branch answers each case before running again.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
Which of these is a valid case selector in the classic language?
