A Better Explanation of Switch Case

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Let me start this thread by thanking those of you who have helped me tremendously over the last week by providing in-depth feedback to my other questions. The time you take on your replies shows a genuine desire on your part to help someone who's learning, and it most certainly is appreciated, so thank you.

Switch Case:

The books for beginners I have, as well as internet searches reveal little information on switch case statements. I understand the usage, some number or variable is tested, and depending on the result, one of a number of instructions are executed. These pieces of literature don't really go into any depths on exactly how they operate though, which is where my questions come from.

For instance, I have only recently figured out through testing that an if statement is not a continuous loop like a while statement. The condition is tested and if true, the if instructions are executed once, and not continuously until the program comes back around to the if statement again (assuming it's still true). I have to assume from this, that the switch case is the same way?

I have seen the switch case examples only in the form of:
Rich (BB code):
switch(x)
{
case 1:
    instruction;
    break;
case 2:
    instruction;
    break;
etc...
}
Can each case contain multiple instructions using braces "{}" or is a case limited to 1 instruction and the break statement.

Finally, for now... what exactly does the "break" statement do?

Thanks
 

MrChips

Joined Oct 2, 2009
30,720
All instruction formats can be considered as a "simple" statement or a "compound" statement.

For example, this is a simple statement:
Rich (BB code):
 x = 123;
A compound statement is a group of statements enclosed by the braces {}, for example:
Rich (BB code):
{
   x = 123;
   y = x/2;
}
Anytime you see a simple statement being used, it can be replaced with a compound statement. So in your example of the switch/case structure

instruction;

can be replaced with
Rich (BB code):
{
  instruction1;
  instruction2;
  instruction3;
}
In a switch/case structure the break; statement is important. This ends the instructions to be executed for that case.
Without the break; the instructions that follow will be executed.

The switch/case structure is essentially a sequence of

Rich (BB code):
if ( x == case1 )
{
}
else if ( x == case2 )
{
}
else if ( x == case3 )
{
}
 

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
MrChips said:
In a switch/case structure the break; statement is important. This ends the instructions to be executed for that case.
Without the break; the instructions that follow will be executed.
So what you're saying is that without the break statement, if the "case" is case 1, after case 1 is executed, the program would just execute case 2, then case 3, etc because "break" isn't there?

If that is true, where does program execution break to? The first instruction after the switch function is closed (i.e. the instruction that follows the switch's right brace)?
 

MrChips

Joined Oct 2, 2009
30,720
With the break; properly in place, after the case is executed, the switch statement ends.

Note that there is also a default option when none of the cases match.
 

spinnaker

Joined Oct 29, 2009
7,830
With the break; properly in place, after the case is executed, the switch statement ends.

Note that there is also a default option when none of the cases match.
Watch that use of "properly" in place. There times when you may not want a break.

Example
Rich (BB code):
switch(code)
{
      case 0:
         DoSomething0();
         break

     case 1:
        DoSomething1();

     case 2:
        DoSomething2();
        break;

      default:
         DoSomethingElse();

}
In this switch statement, if code is 0 then DoSomething0 is executed, if it is 2 then DoSomething2 is executed. But if it is 1 both DoSomething1 and DoSomething 2 are executed. There is nothing wrong with the switch statement if that is your intent.

Also notice the default keyword. DoSomethingElse gets executed if code is not 0,1,2.
 

ErnieM

Joined Apr 24, 2011
8,377
Another thing the switch/case statements can do for you is to use several cases to do the same task. To borrow spin's example:

Rich (BB code):
switch(code)
{
      case 0:
         DoSomethingZer0();
         break;

      case 1:
      case 3:
      case 5:
      case 7:
        DoSomethingOdd();
         break;

      case 2:
      case 4:
      case 6:
      case 8:
        DoSomethingEven();
        break;

      default:
        DoSomethingElse();
        break;
}
Note even and odd numbers do different tasks, as does zero.

Also note the default case has a break too. Why? Good practice and defensive programming when comes the day you add some functions and make a new default with a cut n paste and forget the new break needed.
 

THE_RB

Joined Feb 11, 2008
5,438
Nice example ErnieM, thank you I have never seen that. :)

I wonder how it compiles? The compiler probably inserts a
if x==foo goto blah
for every case instruction?

I've always consider switch/case as a hastily added C kludge by K&R, and a poor way to program. Your example shows an application where for once it might actually be worth using.
 

Tesla23

Joined May 10, 2009
542
Nice example ErnieM, thank you I have never seen that. :)

I wonder how it compiles? The compiler probably inserts a
if x==foo goto blah
for every case instruction?

I've always consider switch/case as a hastily added C kludge by K&R, and a poor way to program. Your example shows an application where for once it might actually be worth using.
With a good compiler the case statement can be highly efficient, almost always much better that comparisons. I would expect even a modestly reasonable compiler to choose one of N case statements using a binary search with O(log2(N)) comparisons, but a good compiler can do even better in some cases. As this article shows
http://www.codeproject.com/Articles/100473/Something-You-May-Not-Know-About-the-Switch-Statem
depending on the label values, the compiler can do much better using jump tables. I know that this article refers to Visual Studio - not an embedded compiler, but there is no reason that embedded compilers couldn't do similar things. I don't have any data on that though. Given that for contiguous case values jump tables are probably more compact than repeated comparisons, they may.
 

WBahn

Joined Mar 31, 2012
29,979
A way that may help you visualize how a switch() statement works is the following.

Let's using the following code snippet:

Rich (BB code):
switch(Fred)
{
   case 0:  statement0;
            statement1;
            break;
   case 2:
   case 4:  statement2;
            statement3;
            break;
   case 1:
   case 3:  statement4;
            statement5;
            break;
   default: statement6;
            statement7;
            break;
}
The body of the switch() statement is copied pretty much as is with each case condition being converted to a label and the break statements being converted to GOTOs to the statement just after the body. The the guts become something like:

Rich (BB code):
   // BODY of switch() statement

   label0:  statement0;
            statement1;
            GOTO endsw;
   label2:
   label4:  statement2;
            statement3;
            GOTO endsw;
   label1:
   label3:  statement4;
            statement5;
            GOTO endsw;
   default: statement6;
            statement7;
            GOTO endsw;
   endsw:

   // CODE following switch()
All that is left is to deal with the switch parameter, Fred. This can be thought of as just a list of IF..GOTO statements that test if Fred is equal to the integer constant associated with each label followed by a GOTO to that label.

Rich (BB code):
   // CONTROL of switch() statement

            IF (Fred == 0) GOTO label0;
            IF (Fred == 2) GOTO label2;
            IF (Fred == 4) GOTO label4;
            IF (Fred == 1) GOTO label1;
            IF (Fred == 3) GOTO label3;
            GOTO default;
   
   // BODY of switch() statement

   label0:  statement0;
            statement1;
            GOTO endsw;
   label2:
   label4:  statement2;
            statement3;
            GOTO endsw;
   label1:
   label3:  statement4;
            statement5;
            GOTO endsw;
   default: statement6;
            statement7;
            GOTO endsw;
   endsw:

   // CODE following switch()
Notice that if none of the conditions are satisfied that there is an unconditional jump to the default section. If there isn't a default section, then you can think of it two ways, either that there is an implied default section that happens to be empty, in which case the goto is effectively a jump to the "endsw" label, or that this goto is changed explicitly to an unconditional jump to "endsw". Both are equivalent.

Compilers can play all kinds of games to make this more efficient, but I believe this conceptually captures all of the behavior of a switch() statement.

Since the case lablels are required to be unique (at least in C), the order of the IF...GOTO statements is irrelevant in the control section, so those can be reordered by the compiler in some manner to permit efficient implementation.

However, note that it because obvious that the order of the case statements is NOT irrelevant. At the end of the day, all you have is a block of code and your switch() parameter is just determining where you jump into this block and, at that point, you just start executing the code from that point on until you encounter a break statement, in which case you jump to the statement after the switch(), or you just naturally fall out the bottom and continue on to the next statement that way.

So consider the following snippet.

Rich (BB code):
switch(Fred)
{
   case 5:  Fred << 1;
   case 4:  Fred << 1;
   case 3:  Fred << 1;
   case 2:  Fred << 1;
   case 1:  Fred << 1;
}
This results in

Fred = Fred * 2^Fred

if Fred is between 1 and 5 inclusive and leaves it unchanged otherwise.

I'm not saying this is useful or even adheres to good style, it's just an example to think of how things can cascade if you don't include break statements. This is useful if you can find a way to leverage it, but more importantly it needs to be kept in mind for when you forget to include a break statement somewhere and are trying to figure out why the code is doing something wonky.

Your ability to debug largely hinges on your ability to correctly recognize what you are telling the code to do (as separate and distinct from what you want the code to do!) and that ability is limited to your understanding of the fundamental behavior of the various programming structures.
 

WBahn

Joined Mar 31, 2012
29,979
Anytime you see a simple statement being used, it can be replaced with a compound statement. So in your example of the switch/case structure
Most of the time, but not anytime. If the value of the statement is needed, then it can't be embedded in a compound statement. This is principally the case with control structures. For instance, in

Rich (BB code):
while(i = f(++x)) loop_code;
the while() statement needs the value of the statement i=f(x). If you turn this into a compound statement, such as

Rich (BB code):
while({++x; i = f(x)}) loop_code;
it won't work.

But if you turn it into a comma delimited statement containing multiple expressions, it will

Rich (BB code):
while(++x, i = f(x)) loop_code;
The comma delimited expression statement is something that most people think they've ever heard of yet most have used it many times. Consider

Rich (BB code):
for (i = 0, j = 1; i < 10; i++) loop_code;
The reason that the initialization statements have to be separated by commas is because a compound statement is not allowed here (although I'm not aware of any reason that it couldn't be allowed both for the first and third statements since, to the best of my knowledge, the value that the statement evaluates to is not used. Obviously it can't be allowed for the 2nd statement since that value IS used.


The switch/case structure is essentially a sequence of

Rich (BB code):
if ( x == case1 )
{
}
else if ( x == case2 )
{
}
else if ( x == case3 )
{
}
As long as it is understood that, in this format, some code might need to be replicated in multiple if() bodies.
 

WBahn

Joined Mar 31, 2012
29,979
Actually, I think I need to modify/retract some of what I just posted.

If we distinguish between "expressions", "expression-statements", and "statement", then it is true that anyplace a statement can be used a compound statement can be used. The subtle point comes in because while an expression can always be a statement, it is not always a statement.

For instance in

x = i + j;

The "x = i + j" is an expression-statement, but in

while (x= i+j) the "x=i+j" is not, it is just an expression.

We tend to think of an assignment "statement" when it is really just an expression in which the operand has a side-effect. I just fell into that trap even though I know better.

So my counter examples in the prior post are not correct because, in those places, while I am using a string of text that, in another context, would be a statement, it is not a statement within the control structures and hence doesn't violate the rule.
 

ErnieM

Joined Apr 24, 2011
8,377
Rich (BB code):
while(++x, i = f(x)) loop_code;
The comma delimited expression statement is something that most people think they've ever heard of yet most have used it many times.
Interesting, but raises a ton of questions.

What is the value? Left most, right most,something in the middle?

What is the order of evaluation? Is x plus-plused before f(x) is evaluated, or the reverse? (Right to left,left to right, or compiler dependent?)

I finally noticed an example of the for statement (perhaps in K&R?) with multiple assignments, but have not had the need for it.
 

WBahn

Joined Mar 31, 2012
29,979
Interesting, but raises a ton of questions.

What is the value? Left most, right most,something in the middle?

What is the order of evaluation? Is x plus-plused before f(x) is evaluated, or the reverse? (Right to left,left to right, or compiler dependent?)

I finally noticed an example of the for statement (perhaps in K&R?) with multiple assignments, but have not had the need for it.
The behavior of compound expressions is well defined by the standard. The expressions are evaluated left to right, all side effects must be resolved before proceeding to the next expression, and the value of the compound expression is equal to the value of the last expression evaluated.
 
Top