Home > Guides > Programming > C/C++

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

switch Statements

Last updated Apr 28, 2006.

switch statements are seemingly a trivial construct that every C++ programmer learns right from the start. However, a deeper glance reveals several tricky issues that could lead to nasty bugs and misunderstandings. In fact, switch statements are among the most error-prone features of C++. However, a few rules of thumb will help you avoid all these bugs.

The Basics

A typical switch statement contains a switch condition in parentheses and a list of one or more case statements. For example:

switch (event)
{
case MOUSE_MOVE:
 //do mouse move;
 break;
case RIGHT_CLICK:
//..
}

Even this tiny example has several potential problems.

A switch condition must have an integral type. This means that you can’t use a switch clause to test strings, pointers or even floating point values. event can therefore be of type int, enum , bool etc, but not float, or void *.

Each case block is associated with a constant integral expression that functions as the case’s label. The label therefore shall not be a variable, or a constant expression that isn’t convertible to int.

Ideally, a case label should have a meaningful name instead of a hardcoded integral value. Enumerated types are a perfect match for labels as they enable the compiler to detect subtle bugs such as the accidental omission of certain cases. Alternatively, constants and even macros (since you insist!) should be used instead of bare numeric values.

Although in certain conditions numeric values used as label make sense, you should avoid them. Here’s why. A colleague of mine once wrote a switch statement that looked like this:

switch (value)
{
case 000:
 //do zero;
 break;
case 010:
//..
 break;
case 100:
//..
 break;
default:
cerr<<"wrong option!";
}

She was puzzled by the outwardly erratic behavior of this code. Can you see what her bug was? Remember: in C and C++, every literal number preceded by 0 is an octal number. My colleague wanted to ensure that all case labels were neatly formatted so she padded them with leading zeroes, not knowing that these leading zeroes caused the labels to be evaluated as octal numbers! Thus, the second label was identical to:

case 8:

This bug would have been avoided had she used a symbolic constant instead:

switch (value)
{
case ZERO:
 //do zero;
 break;
case TEN:
 //..
 break;
case HUNDRED:
 //..
 break;
default:
cerr<<"wrong option!";
}

The default case

The most common bug associated with switch statements is forgetting to include a default: label. As a rule, always add a default: case at the end of your switch statement (default is a reserved keyword) for trapping illegal values or grouping valid values for which a uniform treatment is required:

switch (event)
{
case MOUSE_MOVE:
 //do mouse move;
 break;
case RIGHT_CLICK:
 //..
default:
 cerr<<"unhandled event";
}

Even if you’re 100% sure that the default case is unnecessary, do provide it. Otherwise, your colleagues as well as many automated source code analyzers will balk at its lack.

Falling from Case

In one of their interviews, Brian Kernighan and Dennis Ritchie crowned "falling of the end" of a case statement as their worst design mistake in C. Let’s see what they meant.

case MOUSE_MOVE:
 move_mouse();
case RIGHT_CLICK:
 do_right_click();
case DOUBLE_CLICK:
 do_double_click();

When the switch condition evaluates as MOUSE_MOVE, the matching case block executes, calling move_mouse(). This is exactly what you expect under the circumstances. However, after calling move_mouse(), the execution flow continues from the same location. That is, the program advances to the next case label, executing do_right_click(). Guess what happens next? The DOUBLE_CLICK case block is executed as well, causing do_double_click() to be called. Why is this?

Historically, switch blocks were designed as a goto statement with plenty of syntactic sugar. Therefore, when control is transferred to a label, the execution flow continues from that point until another break statement interrupts the current flow. Programmers who’ve never used goto in their lives find it very confusing; they tend to think of each case as the equivalent of a function call. When the code for a specific case has executed, they expect that control should exit the switch statement, which of course doesn’t happen.

To overcome this idiosyncratic behavior of switch cases, you must provide an explicit break statement at the end of each case label.

case MOUSE_MOVE:
 move_mouse();
 break;
case RIGHT_CLICK:
 do_right_click();
 break;
case DOUBLE_CLICK:
 do_double_click();
 break;

Scope

Because a switch statement is essentially a collection of goto statements with syntactic sugar, they have another counter-intuitive property with respect to the scope of locally-declared objects. Many programmers mistakenly assume that an object declared inside a case block is local to that block. This is incorrect, though. In the following code, the object up is declared in the second case block. However, it’s visible from every other case block. In other words, the scope of up is the entire switch, not just the case in which it’s declared:

case DOWN:
 up.show(); //undefined behavior, up is visible but not yet initialized
 break;
case UP:
 Widget up;
 up.show();
 break;

This property could lead to a serious bug: whereas the scope of the object is the entire switch block, its initialization takes place only once the case in which it’s declared is executed. Therefore, the first case block may access the object up, although the results are undefined since up hasn’t been constructed yet. Every decent compiler nowadays issues a warning in this situation. Alas, many programmers don’t understand this warning, often ignoring it!

To restrict the scope of objects to a case block, always use braces:

case DOWN:
 do_something(); //up is visible from here
 break;
case UP:
{
 Widget up; //inaccessible from other case blocks
 up.show();
 break;
}

Related Resources

There are currently no related podcasts. Please check back later.

Emily Nave#TuesdayTrivia: WindowsRT, Tiles and Charms...Oh My! Win "Building Windows 8 Apps with C# and XAML" eBook by Jeremy Likness
By Emily NaveNovember 6, 2012Comments
Software developers know the thing you can count on in your career is change. Microsoft is redefining the way apps are developed -- are you ready to get on board?

Rachel BaylessInformIT's 17 Days of Giveaways
By Rachel BaylessJuly 3, 2012Comments

Get ready for a month full of giveaways. From July 9 through the end of the month, InformIT will be having 17 days of giveaways. Each week has a theme to make sure that there’s something YOU will be excited to win!

John  TraenkenschuhDennis Ritchie--May He Rest in Peace...
By John TraenkenschuhOctober 16, 2011Comments
Time to Remember a True Computing Pioneer...


See More Blogs