Aussie AI
C++ Switch Statement Bugs
-
Bonus Material for "Generative AI in C++"
-
by David Spuler, Ph.D.
Switch Statement Bugs
The switch statement is the longstanding multi-way branch statement in C++. There are numerous idiosynchrasies in switch statements, some of them by design from the earliest language design choices. Common errors in switch statements include:
- Missing break (case fallthrough)
- Missing case clauses
- Missing default clause
- Commas in case expressions
- Typos in case and default labels
Missing break in a switch statement
Leaving out a break statement in a case clause causes execution to "fall through" to the next case. This is a standardized "feature" of the C++ language design since inception. This causes the statements for more than one case to be executed. If a program appears to be doing two things when it should be only doing one, this could be the cause.
For example, consider the following switch statement to convert an error code to an error message:
switch (err) { case E_TOO_LONG: fprintf(stderr, "String too long\n"); case E_UPPER_CASE: fprintf(stderr, "String should be lower case\n"); default: fprintf(stderr, "Unrecognized error\n"); }
All of the cases should have break statements after the fprintf statement. As above, when err equals E_TOO_LONG the statement will fall through all the cases and also the default case; that is, it prints all three error messages. The corrected switch statement is shown below. Even the default code has a break statement. Although it doesn't strictly need one, this is a safety net in case another case clause is later added to the bottom of the switch statement.
switch (err) { case E_TOO_LONG: fprintf(stderr, "String too long\n"); break; case E_UPPER_CASE: fprintf(stderr, "String should be lower case\n"); break; default: fprintf(stderr, "Unrecognized error\n"); break; }
Missing case in a switch statement
It is a very common error in a switch statement to simply forget a case. The use of switch over a set of numeric codes is a common C++ programming idiom. If you forget to handle one of the codes, it's a bug for that code.
Unfortunately, there's no easy prevention for this error. Sometimes, if your switch is using an enum type, you may get a C++ compilation warning that not all enumerated constants are handled. It's also a common code maintenance error for a new enum code to get added later, with not all the switch statements getting properly updated.
Missing default in a switch statement
Another problem that can appear in a switch statement is failure to cover all cases such as a forgotten case or a missing default clause. It is good practice to ensure that every switch statement has a default code block, and many C++ compilers will emit a warning about a switch without a default clause.
Even if it seems "obvious" that the control value will always match one of the values, it is good defensive programming practice to have a default block that reports an error if this "impossible" situation occurs.
Commas in Case Expressions
The C++ case clause does not allow multiple numbers with commas.
case 5, 7: // Bug
Unfortunately, C++ has a "comma operator" which evaluates "5,7" as an expression equal to "7", and the case of "5" is silently lost, as if the programmer had only written "case 7:". The behavior of a program with this declaration may appear strange. The switch statement will execute the "default" clause for the value 5, leading to erroneous results.
Misspelt Default Label in Switch
A very rare but bizarre error can occur if the programmer mistypes the "default" label. For example, consider the following code:
switch (x) { case 1: printf("case 1"); break; case 2: printf("case 2"); break; defalut: printf("default"); break; // Typo! }
The default label is spelled incorrectly as "defalut" and becomes a goto label by accident. When none of the cases match during program execution, the default code is simply skipped because there is no default label and the program continues with the statements following the switch statement. Fortunately, this error is usually detected by a warning about an unused label "defalut" or unreachable code (the printf call).
Missing Space in a Switch Case Label
Consider this code in a switch:
case 4: // ... case5: // ...
This is a bug where there's clearly a space missing in a case. But this isn't a compilation error, because "case5" is treated as a goto label (yes, C++ has goto statements). You should get a compiler warning about an "unused label", but it will still run.
Unreachable case statements
One dangerous example of an unreachable statement is a missing case label at the start of a switch:
switch(x) { putchar('0'); // Unreachable - missing case .... }
switch case bypasses local variable initialization
This is a confusing C++ compiler message for novices that usually indicates a real coding error. This fortunately gets a compilation error in C++, but it can be an insidious bug in older C programs. A common error involving the switch statement is to initialize local variables in the block containing the cases. The following code shows the error:
switch (x) { int y = 2; // Error case 1: break; case 2: int z = 3; // Error break; default: break; }
The declaration of "y" is always bypassed by the switch statement jumping to a case or default label, and so the initialization may never occur. This is a real bug and gets a C++ compiler error.
Similarly, the initialization of 'z' is skipped by the default clause. C++ prohibits code that bypasses initializations and a compilation error is produced for both this case. However, if "z" is merely being used as a local variable for that case, this isn't a real bug. For a spurious error about a temporary variable used in one case, the solution is often simply to use braces for the case clause, and put the variable initialization inside the braces. This reduces the variable's scope to a single clause, and resolves the compilation error. For example, one of the above problems can be fixed by adding braces:
case 2: { int z = 3; // okay // ... etc break; }