I'll explain the switch statement and then explain
why it exists.
This switch statement:
1 2 3 4 5 6 7 8 9 10 11
|
switch(val) {
case 1:
cout << "1\n";
break;
case 2:
cout << "2\n";
break;
default:
cout << "3\n";
break;
}
|
is basically the same as :
1 2 3 4 5 6 7
|
if (val == 1) {
cout << "1\n";
} else if (val == 2) {
cout << "2\n";
} else {
cout << "3\n";
}
|
Both statements do different things depending on the value of a single expression, in this case,
val
.
You can't use a switch statement in place of all if/else/if ladders. Switch statements actually have several restrictions:
- The test expression (the thing inside the parentheses in
switch(expression) {...
) must evaluate to an integral type (char, int, unsigned int, long, enum, bool etc). It can't evaluate to float or double.
- The case values must be constant.
- It always tests for equality.
And without a
break
statement, program flows from one case down into the next (commonly called "falling through" to the next case). Compare the example above to this:
1 2 3 4 5 6 7 8 9 10 11
|
int val=2;
switch (val) {
case 1:
cout << "1\n";
// fallthrough
case 2:
cout << "2\n";
// fallthrough
default:
cout << "3\n";
}
|
[/code]
The output of this is:
Since val==2, the program goes to case 2 and starts executing. After printing 2, program flow just drops down to the next statement and keeps on going.
As an aside, this "fall through by default" behavior explains a couple of odd looking things about my examples. I always put
break;
or
// fallthrough
at the end of every case to make my intention clear.
From a high level, a switch statement is great when you want to choose from among a set of values, such as when evaluating menu choices. When you read code and see
switch(val)
, you immediately know that the following code is doing different things for different values of val. IN contrast, when you see
if (val ==2)
you don't know if the
else
will be for another value of val, or if it will check a completely different condition, or even if there will be an
else
.
Okay, But WHY??
By now you're probably wondering why on earth the switch statement exists. It has so many restrictions and usually a simple if/else ladder will do the trick. So
why??
One reason is the one above: it communicates your intention to anyone reading your code.
But a bigger reason is that
switch statements can be implemented incredibly efficiently, and the more cases you have, the more efficient it gets. The first example above can be implemented using this high level pseudo-assembly
1 2 3 4 5 6 7 8 9 10 11 12 13
|
// Create an array of addresses.
static label jumpTable[2] = {label 1, label 2};
if (val < 0 || val > 2) goto default;
goto jumpTable[val];
label 1:
cout << "1\n";
goto end;
label 2:
cout << "2\n";
goto end;
label default:
cout << "3\n";
end:
|
Each goto is a single machine instruction. if (val < 0 || val > 2) is probably around 5 instructions. If you had 10 cases instead of 2, lines 3 and 4 would be the same (except
val > 2
would be
val > 10
). So
it would take the same amount of time to jump to the right case. Even if there were 100 cases, the time would be the same because it uses the jump table.
If you're familiar with assembly then it should be clear now why the
case
labels must be constant and why the
switch
expression must evaluate to an integer. Otherwise the compiler couldn't implement it this way.
There are other ways to implement the
switch
. If the case's aren't consecutive numbers (e.g., case 1, case 11, case 45, case 223), then you could do a binary search through the jump table. Or if there are just one or two cases, then you could do a simple if/else ladder. The thing is the compiler is free to choose the implementation. It will evaluate the number of cases, whether they are consecutive etc. and choose the best implementation for that specific switch statement.