Skip to main content

#4. Avoid fall-through in nonempty cases of "switch" statements

Each case label in a switch statement shall be followed by one of the following:
  • Any number of statements, followed by a break instruction.
  • Another case label.
  • A default label.
Do not write statements below a case label and without a break instruction after them. It is confusing.

A switch statement is a controlled jump. Every time the code execution encounters the keyword switch, it will jump to a different place. The question is where. And the answer is:
  • To the case which is equal to the value of the expression in the switch, if there is one;
  • To the default case, if there isn't one of the above and it exists;
  • To the line after the closing bracket in the switch block, otherwise.
In the first case, when the break instruction is reached, the execution flow will jump to the line after the closing bracket in the switch block.

Or should I say "if"? The break instruction is actually optional. You could omit it, and the execution flow would fall through to the instructions after the next case label.

This would be legal, but very confusing. You should avoid it.

Let me quote this StackOverflow answer, which in turn quotes a designer of the C# language argumenting against fall-through between cases being built into the language (C# doesn't have fall-through between cases):
This implicit fall-through behavior is often used to reduce the amount of code needed and often isn't an issue the first time that code is written. However, as code moves from the initial development phase into a maintenance phase, the code above can lead to subtle errors that are very hard to debug. These errors result from the very common mistake of the developer adding a case, yet forgetting to put a break at the end of the block.
In C#, the switch statement requires that explicit flow control occur at the end of a case, either a break, goto, return, or throw. If the developer desires fall-through semantics, it can be achieved by an explicit goto at the end of a case statement.
There is not one single truth, except maybe for the fact that you need to know the syntax of your language and use it correctly. But I am strongly inclined to treat C++ as if it was C# in this particular item: do not use fall-through, and always use break at the end of a nonempty switch statement. This will make your C# and C++ switch statements look the same, and behave in the same way - a robust, less error prone way.

Bibliography

[McConnell 2004]
Steve McConnell: Code Complete, 2nd Edition, Microsoft Press, 2004.

This book discusses switch statements in Chapter 15: "Using conditionals", Section 15.2 "case statements" (pages 361-365).

Comments

Popular posts from this blog

#11. Don't use syntactic overloading

Syntactic overloading (or function overloading) is a C++ feature which allows a class to have several functions with the same name, but with different parameters. To say "Don't use syntactic overloading" is the same as to say "Don't give the same name to different functions".

class C
{
    ...
    void add(int i);
    void add(double d);
    void add(C other);
    ....
}

When you write this client code:

C c;
c.add(x);

The compiler will call the appropiate version of the funcion add, depending on the type of x.

This is what syntactic overloading is. It is called syntactic overloading to distinguish it from semantic overloading 8more properly called dynamic binding), which is the one that happens when you use inheritance and polymorphism.

Syntactic overloading may seem useful at first sight, but it has lots of drawbacks for no real advantage. It just can go wrong in too many ways.

Code is more readable without syntactic overloading. One name for each thing, different n…

#2. Make all type conversions explicit

Assignments, function calls and expressions in general should not contain implicit type conversions. All type conversions should be explicit.

Type conversions should be isolated in their own line, which should consist only of the assignment of the result of the type conversion to a variable of the target type.

A statement should not mix type conversion with other operations. For example, if you need to convert an object to one of another type to pass it as a function parameter, do not do it in the function call itself; instead, do it in a separate instruction in the previous line.
Type conversion occurs when an entity of type A is, well, converted to an entity of type B. This may occur in function calls, expressions using operators, or assignments.
An explicit type conversion is one which you can read in the code. An implicit type conversion is one which does not appear in the source code, but is done automatically when the software is executed.
Making all type conversions explicit he…

#10. Choosing the right loop structure

C++ has three loop instructions: for, while and do ... while. To choose wisely among them, you need to know what their differences are.
1) for A for loop makes sense when you repeat an action for a known number of times, or for all the elements of a known set.

This is the syntax of a forloop:

for (initialization; condition; expression)
{
    statement
}

statement is actually a compound statement, that is, a block of code. This is called the body of the loop. In contrast, the initialization, condition and expression together are called the header of the loop. The initialization is a single statement which ends at the semicolon I wrote after it. The condition is a boolean expression. The expression is a statement. It should be a single statement which modifies one variable which is involved in the condition (see #guideline #5).

The initialization is performed. Then, the condition is evaluated. Then, two things can happen. If the condition is false, the code jumps right after the for statemen…