views:

1037

answers:

7

Given a simple switch statement

switch (int)
{
    case 1 :
    {
        printf("1\n");
        break;
    }

    case 2 : 
    {
        printf("2\n");
    }

    case 3 : 
    {
        printf("3\n");
    }
}

The absence of a break statement in case 2, implies that execution will continue inside the code for case 3. This is not an accident. It was designed that way. Why was this decisions made? What benefit does this provide vs, having an automatic break semantic for the blocks? What was the rational?

+12  A: 

If cases were designed to break implicitly then you couldn't have fallthrough.

case 0:
case 1:
case 2:
    // all do the same thing.
    break;
case 3:
case 4:
    // do something different.
    break;
default:
    // something else entirely.

If the switch was designed to break out implicitly after every case you wouldn't have a choice about it. The switch-case structure was designed the way it is to be more flexible.

Bill the Lizard
You can image a switch that breaks implicitly, but has a "fallthrough" keyword. Awkward, but workable.
dmckee
How would that be better? I'd imagine a case statement to work this way more often than "block of code per case"... that's an if/then/else block.
Bill James
last comment @dmckee
Bill James
I would add that in the question, the scope enclosures {} in each case block are adding to the confusion since it looks like the style of a "while" statement.
MattSmith
@Bill: It would be worse, I think, but it would address the complaint brought up by Mike B: that fall-though (other than multiple cases the same) is a rare event and should not be the default behavior.
dmckee
I left out the braces for brevity, with multiple statements they should be there. I agree that fallthrough should be used only when cases are exactly the same. It's confusing as hell when cases build on previous cases using fallthrough.
Bill the Lizard
+3  A: 

It eliminates code duplication when several cases need to execute the same code (or the same code in sequence).

Since on the assembly language level it doesn't care whether you break between each one or not there is zero overhead for fall through cases anyways, so why not allow them since they offer significant advantages in certain cases.

Greg Rogers
+6  A: 

The case statements in a switch statements are simply labels.

When you switch on a value, the switch statement essentially does a goto to the label with the matching value.

This means that the break is necessary to avoid passing through to the code under the next label.

As for the reason why it was implemented this way - the fall-through nature of a switch statement can be useful in some scenarios. For example:

case optionA:
    // optionA needs to do its own thing, and also B's thing.
    // Fall-through to optionB afterwards.
    // Its behaviour is a superset of B's.
case optionB:
    // optionB needs to do its own thing
    // Its behaviour is a subset of A's.
    break;
case optionC:
    // optionC is quite independent so it does its own thing.
    break;
LeopardSkinPillBoxHat
+3  A: 

To implement duffs device obviously:

dsend(to, from, count)
char *to, *from;
int count;
{
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
FlySwat
Hehehe. Smartass.
dmckee
I love Duff's Device. So elegant, and wicked fast.
dicroce
ya, but does it have to show up every time there's a switch statement on SO?
Bill James
That code is the reason we don't have fallthrough in C# :(
Ishmaeel
You missed two closing curly braces ;-).
Gamecat
+12  A: 

In a lot of ways c is just a clean interface to standard assembly idioms. When writing jump table driven flow control, the programmer has the choice of falling through or jumping out of the "control structure", and a jump out requires an explicit instruction.

So, c does the same thing...

dmckee
A: 

As many here have specified, it's to allow a single block of code to work for multiple cases. This should be a more common occurrence for your switch statements than the "block of code per case" you specify in your example.

If you have a block of code per case without fall-through, perhaps you should consider using an if-elseif-else block, as that would seem more appropriate.

Bill James
+12  A: 

Many answers seem to focus on the ability to fall through as the reason for requiring the break statement.

I believe it was simply a mistake, due largely because when C was designed there was not nearly as much experience with how these constructs would be used.

Peter Van der Linden makes the case in his book "Expert C Programming":

We analyzed the Sun C compiler sources to see how often the default fall through was used. The Sun ANSI C compiler front end has 244 switch statements, each of which has an average of seven cases. Fall through occurs in just 3% of all these cases.

In other words, the normal switch behavior is wrong 97% of the time. It's not just in a compiler - on the contrary, where fall through was used in this analysis it was often for situations that occur more frequently in a compiler than in other software, for instance, when compiling operators that can have either one or two operands:

switch (operator->num_of_operands) {
    case 2: process_operand( operator->operand_2);
              /* FALLTHRU */

    case 1: process_operand( operator->operand_1);
    break;
}

Case fall through is so widely recognized as a defect that there's even a special comment convention, shown above, that tells lint "this is really one of those 3% of cases where fall through was desired."

I think it was a good idea for C# to require an explicit jump statement at the end of each case block (while still allowing multiple case labels to be stacked - as long as there's only a single block of statements). In C# you can still have one case fall through to another - you just have to make the fall thru explicit by jumping to the next case using a goto.

It's too bad Java didn't take the opportunity to break from the C semantics.

Michael Burr
An insightful and interesting comment. I learnt something here. Thanks and well done.
Tom Leys
Indeed, I think they went for simplicity of implementation. Some other languages supported more sophisticated cases (ranges, multiple values, strings...) at the cost, perhaps, of efficiency.
PhiLho
Java probably didn't want to break habits and spread confusion. For a different behavior, they would have to use different semantics. Java designers lost a number of opportunities to break from C, anyway.
PhiLho
@PhiLho - I think you're probably closest to the truth with "simplicity of implementation".
Michael Burr
Very insightful answer, thank you. I wasn't aware that C# required explicit jumps, that's good to know.
Matt Olenik
If you're using explicit jumps via GOTO, isn't it just as productive to just use a series of IF statements?
In this case the goto's are used really as an indication that you want the case to fall-through (similar to lint's /* FALLTHRU */ markers). It should be a rather exceptional situation since cases that fall through occur only about 3% of the time (according to the analysis in Van der Linden's book).
Michael Burr