156

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?

seh
  • 14,999
  • 2
  • 48
  • 58
EvilTeach
  • 28,120
  • 21
  • 85
  • 141

9 Answers9

167

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
  • 333,147
  • 50
  • 533
  • 760
  • 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 Oct 31 '08 at 06:29
  • 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 Oct 31 '08 at 06:30
  • @PhiLho - I think you're probably closest to the truth with "simplicity of implementation". – Michael Burr Oct 31 '08 at 18:10
  • If you're using explicit jumps via GOTO, isn't it just as productive to just use a series of IF statements? – DevinB Apr 13 '09 at 12:26
  • 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 Apr 13 '09 at 17:07
  • 3
    even Pascal implement their switch without break. how could C compiler-coder don't think about it @@ – Chan Le May 28 '11 at 14:41
  • @PhiLho Well... Now we have things like pattern matching, enhanced switch statements, and switch expressions; and now the arrow switch does not require break, so I guess this isn't the case (ha) anymore. (Ok I'll see myself out) – LittleWhole May 23 '22 at 01:15
33

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 --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • 3
    I know a lot of people say that, but I think it's not the complete picture; C is also often an ugly interface to assembly antipatterns. 1. Ugly: Statements instead of expressions. "Declaration reflects use." Glorified copy-paste as *the* mechanism for modularity and portability. Type system *way* too complicated; *encourages* bugs. `NULL`. (Continued in next comment.) – Harrison May 17 '12 at 22:30
  • 2
    2. Antipatterns: Doesn't provide "clean" tagged unions, one of the two fundamental data-representation idioms of assembly. (Knuth, vol 1, ch 2) Instead, "untagged unions," a non-idiomatic hack. This choice has hobbled the way people think of data for decades. [And `NUL`-terminated strings are just about the worst idea ever.](http://queue.acm.org/detail.cfm?id=2010365) – Harrison May 17 '12 at 22:33
  • @HarrisonKlaperman: No method of storing strings is perfect. The vast majority of problems associated with null-terminated strings would not occur if routines which accepted strings also accepted a maximum-length parameter, and would occur with routines that store length-marked strings into fixed-sized buffers without accepting a buffer-size parameter. The design of the switch statement where cases are simply labels may seem odd to modern eyes, but it's no worse than the design of the Fortran `DO` loop. – supercat Jul 16 '12 at 16:08
  • If I decide to write a jump table in assembly, I will take the case value, and magically turn that into the jump table subscript, jump to that location, and execute the code. After that I will not jump into the next case. I will jump to a uniform address that is the exit of all of the cases. The idea that I would jump or fallthough into the body of the next case is silly. There is no use case for that pattern. – EvilTeach Mar 16 '14 at 19:55
  • 2
    While nowadays people are used more to clean and self-protecting idos and language features that prevent oneself from shooting into the foot, this is a reminescence from an area where bytes had been expensive (C started back before 1970). if your code needs to fit within 1024 bytes, you will experience heavy preasure to reuse code fragments. Reusing code by starting at different entrypoints sharing the same end is one mechanism for achieving this. – rpy Apr 12 '16 at 06:35
26

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);
    }
}
José
  • 128
  • 5
FlySwat
  • 172,459
  • 74
  • 246
  • 311
19

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
  • 398,270
  • 210
  • 566
  • 880
  • 13
    You can image a switch that breaks implicitly, but has a "fallthrough" keyword. Awkward, but workable. – dmckee --- ex-moderator kitten Oct 31 '08 at 03:16
  • 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. – billjamesdev Oct 31 '08 at 05:25
  • 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. – Matthew Smith Oct 31 '08 at 06:21
  • 1
    @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 --- ex-moderator kitten Oct 31 '08 at 12:16
  • 1
    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 Oct 31 '08 at 12:40
  • Alternatively, you could have a separate statement called something like "doswitch" which allows fallthrough, while the default switch does not. For the same reason that we have do-while loops that rarely get used compared to the regular while loop. – saolof Mar 03 '17 at 21:06
18

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
  • 28,915
  • 15
  • 75
  • 111
  • 2
    There are two big problems: 1) Forgetting the `break` where it is needed. 2) If the case statements have their order changed, the fallthrough can result in the wrong case being run. Thus, I find C#'s handling much better (explicit `goto case` for fallthrough, except for empty case labels). – Daniel Rose Aug 28 '12 at 13:38
  • @DanielRose: 1) there are ways to forget `break` in C# too - simplest one is when you don't want a `case` to do anything but forget to add a `break` (perhaps you got so wrapped up in the explanatory comment, or got called away on some other task): execution will just fall through to the `case` below. 2) encouraging `goto case` is encouraging unstructured "spaghetti" coding - you can end up with accidental cycles (`case A: ... goto case B; case B: ... ; goto case A;`), especially when the cases are separated in the file and hard to grok in combination. In C++, fall-through is localised. – Tony Delroy Jun 27 '16 at 10:20
9

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
3

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
  • 35,641
  • 17
  • 67
  • 94
3

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);
}
Martti K
  • 31
  • 1
0

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.

billjamesdev
  • 14,554
  • 6
  • 53
  • 76