85

Who decided (and based on what concepts) that switch construction (in many languages) has to use break in each statement?

Why do we have to write something like this:

switch(a)
{
    case 1:
        result = 'one';
        break;
    case 2:
        result = 'two';
        break;
    default:
        result = 'not determined';
        break;
}

(noticed this in PHP and JS; there are probably many other languages using this)

If switch is an alternative of if, why we can't use the same construction as for if? I.e.:

switch(a)
{
    case 1:
    {
        result = 'one';
    }
    case 2:
    {
        result = 'two';
    }
    default:
    {
        result = 'not determined';
    }
}

It is said that break prevents the execution of the block following the current one. But, does someone really run into the situation, where there was any need for execution of the current block and following ones? I didn't. For me, break is always there. In every block. In every code.

trejder
  • 2,386
  • 3
  • 19
  • 39
  • 1
    As most of the answers have noted, this is related to 'fall-through', where multiple conditions are routed through the same 'then' blocks. However, this is language dependent - RPG, for example, treats a `CASE` statement equivalently to a giant if/elseif block. – Clockwork-Muse Aug 28 '12 at 15:51
  • 36
    It was a poor decision made by the C-designers *(like many decisions, it was made to make the transition from assembly --> C, and the translation back, easier, rather than for ease of programming)*, which was then unfortunately inherited in other C-based languages. Using the simple rule **"Always make the common case the default!!"**, we can see that the default behaviour *should* have been to break, with a separate keyword for falling through, since that is by far the most common case. – BlueRaja - Danny Pflughoeft Aug 28 '12 at 16:25
  • 4
    I have used fall-through on several occasions, though arguably the switch statement in my preferred language at the time (C) could have been designed to avoid it; e.g. I have done `case 'a': case 'A': case 'b': case 'B'` but mostly because I cannot do `case in [ 'a', 'A', 'b', 'B' ]`. A slightly better question is, in my current preferred language (C#), the break is *mandatory*, and there *is no* implicit fall-though; forgetting `break` is a syntax error... :\ – KutuluMike Aug 28 '12 at 18:20
  • 1
    Fall-through with actual code is often found in parser implementations. `case TOKEN_A: /*set flag*/; case TOKEN_B: /*consume token*/; break; case TOKEN_C: /*...*/` – OrangeDog Aug 28 '12 at 19:25
  • @MichaelEdenfield - ugh, this is one of my c# pet peeves too. If you're going to enforce it in order to "protect me", at least provide a way to do the fall through when I choose to (eg. allow a `continue` statement in place of `break` to explicitly fall through). – Alconja Aug 29 '12 at 00:14
  • 1
    @BlueRaja-DannyPflughoeft: C was also designed to be easy to compile. “Emit a jump if `break` is present anywhere” is a significantly simpler rule to implement than “Don’t emit a jump if `fallthrough` is present in a `switch`”. – Jon Purdy Aug 29 '12 at 00:36
  • @Alconja you can `goto` case labels within the same switch statement, but you *must* somehow explicitly exit each case block (`break`, `return`, or `goto`) and, personally, I think that overloading of `goto` was a poor choice. – KutuluMike Aug 29 '12 at 00:51
  • @Jon *"And the translation back [to assembly]"* would be compiling. – BlueRaja - Danny Pflughoeft Aug 29 '12 at 04:09
  • OK, now I have this picture of a language that would do "break" by default (no keyword needed) and have a "fallthrough;" keyword to explicitely continue the flow. @Language designers : please add this in your language ASAP :-) – Joan Charmant Aug 29 '12 at 08:57
  • Pascal explicitly forces the case statement to follow only one clause. IMHO the break in C is a mistake as it allows for many errors and doesn't speed up the common usages. – Michael Shopsin Aug 29 '12 at 13:33
  • @BlueRaja - Danny Pflughoeft : 50 years down the track with 20/20 hindsight we may be able to say it was the wrong decision, but to state it was a poor decision is arrogant at best - who are you to be so bold? Whats your background in Computer History and language design. – mattnz Sep 04 '12 at 22:15
  • 1
    @BlueRaja-DannyPflughoeft: C was modeled on BCPL, which has implicit fall-through unless the `endcase` keyword is used. – TMN Sep 05 '12 at 13:32
  • https://craftofcoding.wordpress.com/2017/05/16/history-of-the-horrid-switch-statement-ii-algol-68/ Especially Algol68 and Pascal had a **case** statement. – Joop Eggen Mar 13 '19 at 15:08

11 Answers11

100

C was one of the first languages to have the switch statement in this form, and all other major languages inherited it from there, mostly choosing to keep the C semantics per default - they either didn't think of the advantages of changing it, or judged them less important than keeping the behaviour everyone was used to.

As for why C was designed that way, it probably stems from the concept of C as "portable assembly". The switch statement is basically an abstraction of a branch table, and a branch table also has an implicit fall-through and requires an additional jump instruction to avoid it.

So basically, the designers of C also chose to keep the assembler semantics per default.

Michael Borgwardt
  • 51,037
  • 13
  • 124
  • 176
  • 3
    VB.NET is example of an language that did change it. There is no danger of the having it flow into the case clause. – poke Aug 28 '12 at 13:30
  • 1
    Tcl's another language that doesn't ever fall through to the next clause. Virtually never wanted to do it either (unlike in C where it's useful when writing certain types of programs). – Donal Fellows Aug 28 '12 at 19:01
  • 4
    C's ancestor languages, [B](http://cm.bell-labs.com/cm/cs/who/dmr/kbman.pdf) and the older [BCPL](http://cm.bell-labs.com/cm/cs/who/dmr/bcpl.pdf), both have (had?) switch statements; BCPL spelled it `switchon`. – Keith Thompson Aug 28 '12 at 22:13
  • Ruby's case statements don't fall through; you can get similar behavior by having two test values in a single `when`. – Nathan Long Aug 29 '12 at 12:52
  • It is more or less an accident. See historical research here https://stackoverflow.com/a/2710698/2160440 – wlnirvana Mar 06 '20 at 13:22
91

Because switch is not an alternative of if ... else statements in those languages.

By using switch, we can match more than one condition at a time, which is highly appreciated in some cases.

Example:

public Season SeasonFromMonth(Month month)
{
    Season season;
    switch (month)
    {
        case Month.December:
        case Month.January:
        case Month.February:
            season = Season.Winter;
            break;

        case Month.March:
        case Month.April:
        case Month.May:
            season = Season.Spring;
            break;

        case Month.June:
        case Month.July:
        case Month.August:
            season = Season.Summer;
            break;

        default:
            season = Season.Autumn;
            break;
    }

    return season;
}
Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
Satish Pandey
  • 1,340
  • 3
  • 10
  • 20
  • Hm... All books I've read said: `switch` IS an alternative of `if`. And most of them also said, that situation, where you execute (match, evaluate) more than one block of code is very rare and unwanted in most cases. – trejder Aug 28 '12 at 09:30
  • 15
    `more than one block ... unwanted in most cases` I am not agree with you. – Satish Pandey Aug 28 '12 at 09:36
  • 1
    @SatishPandey I think the counter argument to allowing fall-through is that it can cause expensive, difficult to find bugs when used unintentionally. For example, see [Douglas Crockford's opinion on the matter](http://www.yuiblog.com/blog/2007/04/25/id-rather-switch-than-fight/). There are also (uncanny) practical uses of it, such as [Duff's Device](http://en.wikipedia.org/wiki/Duff's_device). I tend not to use long switch statements, so I don't really have a strong opinion either way. – Daniel B Aug 28 '12 at 10:00
  • I think we only need to consider not to forgot to apply `break;` statement after finishing with a particular operation. This only happens if someone is new to `switch .. case` and i don't think we ignore this important construct because of this reason. I use `switch` when required but never get any issues with it. – Satish Pandey Aug 28 '12 at 10:12
  • 2
    @DanielB It depends on the language though; C# switch statements don't allow for that possiblity. – Andy Aug 28 '12 at 11:55
  • 5
    @Andy Are we talking about fall-through still? C# switch statements [definitely allow for it](http://msdn.microsoft.com/en-us/library/06tc147t(v=vs.80).aspx) (check the 3rd example), hence the need for `break`. But yes, as far as I'm aware, Duff's Device does not work in C#. – Daniel B Aug 28 '12 at 12:02
  • 8
    @DanielB Yes we are, but the compiler won't allow a condition, code, and then another condition without a break. You can have mutliple conditions stacked with no code between, or you need a break. From your link `C# does not support an implicit fall through from one case label to another`. You can do fall through, but there's no chance of accidently forgetting a break. – Andy Aug 28 '12 at 12:10
  • 1
    @Andy I see what you mean, good point. – Daniel B Aug 28 '12 at 12:41
  • 1
    You argue that switches are not if-else, but your example is nothing but an if-else.. – Matsemann Aug 28 '12 at 14:22
  • 3
    @Matsemann .. I now we can do all the stuff using `if..else` but in some situations `switch..case` would be preferable. The above example would be bit complex if we use if-else. – Satish Pandey Aug 28 '12 at 14:54
  • 2
    -1: This answer is neither responsive to the original question nor correct. It is unresponsive in that it does not speak to "break", which is necessitated by implicit fallthrough, which STARTED with C. It is incorrect in that other languages allow multiple case labels on a single switch arm. (PASCAL separates them with commas. Ada separates with vertical bars, as I recall.) – John R. Strohm Aug 28 '12 at 20:48
  • 3
    @DanielB: C# actually does not allow fall through at all, what it allows is having a case with multible labels. Note how the [updated version](http://msdn.microsoft.com/en-us/library/06tc147t.aspx) of the same page does not refer to fall through anymore, as that was misleading terminology for the C# case. – Joren Aug 29 '12 at 08:19
  • @Joren agreed, and I admitted as much after Andy's comment :) – Daniel B Aug 29 '12 at 09:01
  • C# *can* do fall-through, just not *implicit* fall-through. See this Stack Overflow answer: http://stackoverflow.com/a/174223/108464. It's annoying, true, but if you end up reordering your case statements, you can't break anything. – Daniel Joseph Aug 29 '12 at 16:54
  • 1
    your seasons are all wrong ... (obviously, you live on the OTHER side of Equator :) – Noctis Sep 24 '14 at 00:33
18

This has been asked on Stack Overflow in the context of C: Why was the switch statement designed to need a break?

To summarize the accepted answer, it was probably a mistake. Most other languages have probably just followed C. However, some languages such as C# seem to have fixed this by allowing fall-through – but only when the programmer explicitly tells so (source: the link above, I don't speak C# myself).

Tapio
  • 301
  • 1
  • 5
  • Google Go does the same thing IIRC (allowing fallthrough if explicitly specified). – Leo Aug 28 '12 at 11:56
  • PHP does, also. And the more you look at the resulting code, the uneasier it makes you feel. I like the `/* FALLTHRU */` comment in the answer linked by @Tapio above, to _prove_ you meant it. – DaveP Aug 28 '12 at 13:29
  • 1
    Saying C# has `goto case`, as the link does, doesn't illustrate why that works so well. You can still stack multiple cases like above, but as soon as you insert code, you have to have an explicit `goto case whatever` to fall through to the next case or you get a compiler eror. If you ask me, of the two, the ability to stack cases without worrying about inadvertent fall-through through negligence is by far a better feature than enabling explicit fall-through with `goto case`. – Jesper Aug 28 '12 at 14:48
10

I will answer with an example. If you wanted to list the number of days for each month of a year, it is obvious that some months have 31, some 30, and 1 28/29. It would look like this,

switch(month) {
    case 4:
    case 6:
    case 9:
    case 11;
        days = 30;
        break;
    case 2:
        //Find out if is leap year( divisible by 4 and all that other stuff)
        days = 28 or 29;
        break;
    default:
        days = 31;
}

This is an example where multiple cases have the same effect and are all grouped together. There was obviously a reason for the choice of the break keyword and not the if ... else if construct.

The main thing to take note here is that a switch statement with many similar cases is not an if ... else if ... else if ... else for each of the cases, but rather if(1, 2, 3) ... else if(4,5,6) else ...

OrangeDog
  • 141
  • 1
  • 8
Awemo
  • 289
  • 1
  • 4
  • 2
    Your example looks more verbose than an `if` without any benefit. Perhaps if your example assigned a name or did month-specific work in addition to the fall-through, the utility of fall-through would be more apparent? (without commenting on whether one SHOULD in the first place) – horatio Aug 28 '12 at 18:26
  • 1
    That is true. I was however just attempting to show cases where fall through could be used. It would obviously be much better if each of the months needed something done individually. – Awemo Aug 29 '12 at 09:21
8

There are two situations where “fall through” from one case to another can happen - the empty case:

switch ( ... )
{
  case 1:
  case 2:
    do_something_for_1_and_2();
    break;
  ...
}

and the non-empty case

switch ( ... )
{
  case 1:
    do_something_for_1();
    /* Deliberately fall through */
  case 2:
    do_something_for_1_and_2();
    break;
...
}

Notwithstanding the reference to Duff's Device, legitimate instances for the second case are few and far between, and generally prohibited by coding standards and flagged during static analysis. And where it is found, it is more often than not due to the omission of a break.

The former is perfectly sensible and common.

To be honest, I can see no reason for needing the break and the language parser knowing that an empty case-body is a fall through, while a non-empty case is standalone.

It is a pity that the ISO C panel seem more preoccupied with adding new (unwanted) and badly defined features to the language, rather than fixing the undefined, unspecified or implementation-defined features, not to mention the illogical.

amon
  • 132,749
  • 27
  • 279
  • 375
Andrew
  • 2,018
  • 2
  • 16
  • 27
4

Not forcing the break allows a number of things that could otherwise be difficult to do. Others have noted grouping cases, for which there are a number of non-trivial cases.

One case where it is imperative that the break not be used is Duff's Device. This is used to "unroll" loops where it can speed up operations by limiting the number of comparisons required. I believe the initial use allowed functionality which had previously been too slow with a fully rolled up loop. It trades of code size for speed in some cases.

It is good practice to replace the break with an appropriate comment, if the case has any code. Otherwise, someone will fix the missing break, and introduce a bug.

trejder
  • 2,386
  • 3
  • 19
  • 39
BillThor
  • 6,232
  • 17
  • 17
4

In C, where the origin seems to be, the code block of the switch statement is not a special construct. It is a normal block of code, just as a block under an if statement.

switch ()
{
}

if ()
{
}

case and default are jump labels inside this block, specifically related to switch. They are handled just as normal jump labels for goto. There is one specific rule that is important here: Jump labels can be nearly everywhere in the code, without interrupting the code flow.

As a normal code block, it doesn't need to be a compound statement. The labels are optional, too. These are valid switch statements in C:

switch (a)
    case 1: Foo();

switch (a)
    Foo();

switch (a)
{
    Foo();
}

The C standard itself gives this as an example (6.8.4.2):

switch (expr) 
{ 
    int i = 4; 
    f(i); 
  case 0: 
    i=17; 
    /*falls through into default code */ 
  default: 
    printf("%d\n", i); 
} 

In the artificial program fragment, the object whose identifier is i exists with automatic storage duration (within the block) but is never initialized, and thus if the controlling expression has a nonzero value, the call to the printf function will access an indeterminate value. Similarly, the call to the function f cannot be reached.

Furthermore, default is a jump label, too, and thus can be anywhere, without the need to be the last case.

This also explains Duff's Device:

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);
}

Why the fall-through? Because in the normal code flow in a normal block of code, fall-through to the next statement is expected, just as you would expect it in an if code block.

if (a == b)
{
    Foo();
    /* "Fall-through" to Bar expected here. */
    Bar();
}

switch (a)
{
    case 1: 
        Foo();
        /* Automatic break would violate expected code execution semantics. */
    case 2:
        Bar();
}

My guess is that the reason for this was ease of implementation. You don't need special code for parsing and compiling a switch block, caring for special rules. You just parse it like any other code and only have to care for the labels and the jump selection.

An interesting follow-up question from all this is if the following nested statements print "Done." or not.

int a = 10;

switch (a)
{
    switch (a)
    {
        case 10: printf("Done.\n");
    }
}

The C standard cares for this (6.8.4.2.4):

A case or default label is accessible only within the closest enclosing switch statement.

Secure
  • 1,918
  • 12
  • 10
1

To answer two of your questions.

Why does C need breaks?

It comes down to Cs roots as a "portable assembler". Where psudo code like this was common:-

    targets=(addr1,addr2,addr3);
    opt = 1  ## or 0 or 2
switch:
    br targets[opt]  ## go to addr2 
addr1:
    do 1stuff
    br switchend
addr2:
    do 2stuff
    br switchend
addr3
    do 3stuff
switchend:
    ......

the switch statement was designed to provide similar functionality at a higher level.

Do we ever have switches without breaks?

Yes this is quite common and there are a few use cases;

Firstly you may want to take the same action for several cases. We do this by stacking the cases on top of each other:

case 6:
case 9:
    // six or nine code

Another use case common in state machines is that after processing one state we immediately want to enter and process another state:

case 9:
    crashed()
    newstate=10;
case 10:
    claim_damage();
    break;
trejder
  • 2,386
  • 3
  • 19
  • 39
James Anderson
  • 18,049
  • 1
  • 42
  • 72
1

Several people have already mentioned the notion of matching multiple conditions, which is very valuable from time to time. However the ability to match multiple conditions does not necessarily require that you do the exact same thing with each condition that matches. Consider the following:

switch (someCase)
{
    case 1:
    case 2:
        doSomething1And2();
        break;

    case 3:
        doSomething3();

    case 4:
        doSomething3And4();
        break;

    default:
        throw new Error("Invalid Case");
}

There are two different ways sets of multiple conditions are being matched here. With conditions 1 and 2, they simply fall through to the exact same plot of code and do the exact same thing. With conditions 3 and 4, however, although they both end by calling doSomething3And4(), only 3 calls doSomething3().

Panzercrisis
  • 3,145
  • 4
  • 19
  • 34
  • Late to the party, but this is absolutely not "1 and 2" as the function name suggests: there are *three* different states that can all lead to the code in `case 2` triggering, so if we want to be correct (and we do) the real function name would have to be `doSomethingBecause_1_OR_2_OR_1AND2()` or something (although legitimate code in which an immutable matches two different cases while still being well-written code is virtually nonexistent, so at the very least this should be `doSomethingBecause1or2()`) – Mike 'Pomax' Kamermans Dec 12 '17 at 20:10
  • In other words, `doSomething[ThatShouldBeDoneInTheCaseOf]1And[InTheCaseOf]2()`. It's not referring to logical and/or. – Panzercrisis Dec 12 '17 at 20:13
  • I know it's not, but given why people get confused over this kind of code, it should absolute explain what that "and" is, because relying on someone to guess "natural and" vs. "logical end" in an answer that only works when precisely explained, needs more explanation in the answer itself, or a rewrite of the function name to remove that ambiguity =) – Mike 'Pomax' Kamermans Dec 12 '17 at 21:19
-2

Break statement is a jumping statement that allows the user to exit the nearest enclosing switch (for your case), while, do, for or foreach. it's just as easy as that.

jasper
  • 1
-3

I think that with all written above - the main outcome of this thread is that if you are to design a new language - the default should be that you do not need to add a break statement and the compiler will treat it as if you did.

If you want that rare case where you want to continue on to the next case - simply state it with a continue statement.

This can be improved so that only if you use curly brackets inside the case then it doesn't go on so that the example with the months above of several cases performing the same exact code - would always work as expected without needing the continue.

ethans
  • 1
  • 2
    I am not sure I understand your suggestion relative to the continue statement. Continue has a very different meaning in C and C++ and if a new language was like C, but used the keyword sometimes as with C and sometimes in this alternative way, I think confusion would reign. While there may be some issues with falling from one labelled block into another, I like the use of multiple cases with the same handling with a shared block of code. – DeveloperDon Oct 05 '12 at 01:17
  • C has a long tradition of using the same keywords for multiple purposes. Think of `*`, `&`, `static` etc. – idrougge May 23 '17 at 22:17