Why was the switch statement designed to need a break?

CLanguage Design

C Problem Overview


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 rationale?

C Solutions


Solution 1 - C

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.

Solution 2 - C

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...

Solution 3 - C

To implement Duff's 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);
    }
}

Solution 4 - C

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.

Solution 5 - C

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;

Solution 6 - C

To allow things like:

switch(foo) {
case 1:
    /* stuff for case 1 only */
    if (0) {
case 2:
    /* stuff for case 2 only */
    }
    /* stuff for cases 1 and 2 */
case 3:
    /* stuff for cases 1, 2, and 3 */
}

Think of the case keyword as a goto label and it comes a lot more naturally.

Solution 7 - C

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.

Solution 8 - C

I happened to run in to a case of assigning values in vectors to structs: it had to be done in such a manner that if the data vector was shorter than the number of data members in the struct, the rest of the members would remain in their default value. In that case omitting break was quite useful.

switch (nShorts)
{
case 4: frame.leadV1    = shortArray[3];
case 3: frame.leadIII   = shortArray[2];
case 2: frame.leadII    = shortArray[1];
case 1: frame.leadI     = shortArray[0]; break;
default: TS_ASSERT(false);
}

Solution 9 - C

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEvilTeachView Question on Stackoverflow
Solution 1 - CMichael BurrView Answer on Stackoverflow
Solution 2 - Cdmckee --- ex-moderator kittenView Answer on Stackoverflow
Solution 3 - CFlySwatView Answer on Stackoverflow
Solution 4 - CBill the LizardView Answer on Stackoverflow
Solution 5 - CLeopardSkinPillBoxHatView Answer on Stackoverflow
Solution 6 - CR.. GitHub STOP HELPING ICEView Answer on Stackoverflow
Solution 7 - CGreg RogersView Answer on Stackoverflow
Solution 8 - CMartti KView Answer on Stackoverflow
Solution 9 - CbilljamesdevView Answer on Stackoverflow