67

This question in mainly pointed at C/C++, but I guess other languages are relevant as well.

I can't understand why is switch/case still being used instead of if/else if. It seems to me much like using goto's, and results in the same sort of messy code, while the same results could be acheived with if/else if's in a much more organized manner.

Still, I see these blocks around quite often. A common place to find them is near a message-loop (WndProc...), whereas these are among the places when they raise the heaviest havoc: variables are shared along the entire block, even when not propriate (and can't be initialized inside it). Extra attention has to be put on not dropping break's, and so on...

Personally, I avoid using them, and I wonder wether I'm missing something?

Are they more efficient than if/else's? Are they carried on by tradition?

a3f
  • 8,517
  • 1
  • 41
  • 46
OB OB
  • 3,289
  • 6
  • 21
  • 16

15 Answers15

174

Summarising my initial post and comments - there are several advantages of switch statement over if/else statement:

  1. Cleaner code. Code with multiple chained if/else if ... looks messy and is difficult to maintain - switch gives cleaner structure.

  2. Performance. For dense case values compiler generates jump table, for sparse - binary search or series of if/else, so in worst case switch is as fast as if/else, but typically faster. Although some compilers can similarly optimise if/else.

  3. Test order doesn't matter. To speed up series of if/else tests one needs to put more likely cases first. With switch/case programmer doesn't need to think about this.

  4. Default can be anywhere. With if/else default case must be at the very end - after last else. In switch - default can be anywhere, wherever programmer finds it more appropriate.

  5. Common code. If you need to execute common code for several cases, you may omit break and the execution will "fall through" - something you cannot achieve with if/else. (There is a good practice to place a special comment /* FALLTHROUGH */ for such cases - lint recognises it and doesn't complain, without this comment it does complain as it is common error to forgot break).

Thanks to all commenters.

qrdl
  • 34,062
  • 14
  • 56
  • 86
  • 12
    +1 on mention of jump table (for dense values). Or a binary search, if the values are sparse. :-) – C. K. Young Jun 22 '09 at 17:31
  • 4
    +1,. also, order doesn't matter in switch, which makes it nice in some cases, like switch(x) { default: assert(0 && "dunno!"); other-cases-here }; – Johannes Schaub - litb Jun 22 '09 at 17:34
  • 1
    @litb: I'm preety sure you're wrong: switch(a){case 1: printf("1"); case 2: printf("2"); break;} would print "12" for a==1, so order does matter. – OB OB Jun 22 '09 at 17:36
  • 1
    @OB OB: Order doesn't matter if "best practice" of no-fallthrough is used. – C. K. Young Jun 22 '09 at 17:37
  • Order matters if you're not breaking after every case. But that type of code should be used sparingly. It is not what most programmers expect – jkerian Jun 22 '09 at 17:39
  • 7
    @OB In case of if/else you need to place more likely cases first to minimise number of tests performed. In case of switch you don't need to worry about that. – qrdl Jun 22 '09 at 17:39
  • 5
    This would take a really messed up compiler to still be true (It may be true, but someone might want to check the assembly after compiling with O4). My guess is that the primary reason is that people still THINK it performs better. – Bill K Jun 22 '09 at 17:43
  • 2
    @Bill K: I'm not sure why you think it would require a messed up compiler to have an optimization that's been around since at least the 80s. It should be mentioned that most switch/case blocks are unable to be optimized in that fashion, but some can be. The biggest reason for sticking with switch/case is that it is extremely clear syntax... follow one of x paths, determined strictly by the value of variable Y. – jkerian Jun 22 '09 at 17:46
  • 1
    I think what Bill K meant was that a good compiler is likely to optimize if/else if/else the same way as switch/case. – OB OB Jun 22 '09 at 17:51
  • @OB OB, i'm sorry. What i meant was the order of default and other labels, only with regard to jumping to the code. While with if/else, the else have to be at the end, with switch, you can put the default at the beginning. It's right that once switch jumps to one label, control will flow without breaking, of course :) – Johannes Schaub - litb Jun 22 '09 at 17:53
  • How compiler could possible know which test is more likely? Let's say I'm testing characters read from file - I know that EOF is least likely case but compiler doesn't know that. – qrdl Jun 22 '09 at 17:55
  • 1
    @qrdl: The point is: aren't today's compilers samrt enough to covert if/else if/else to swtich/case, when optimization benefits from it? – OB OB Jun 22 '09 at 18:01
  • 1
    @qrdl: Actually, on your point about the programmer knowing the "more likely path", some compilers (e.g., gcc) allow you to specify that, and it will provide branch prediction hints to the CPU. :-P – C. K. Young Jun 22 '09 at 18:10
  • 17
    @Bill K: I just tested it with GCC, and even with -O3, it doesn't optimize the if/else chain into a jump table, whereas with a switch it does even without optimizations on. Other compilers' mileage may vary. – Greg Rogers Jun 22 '09 at 18:18
  • 1
    Heck, I'm not surprised. Up to -O3 java is pretty much as fast as C. -O4 it tends to blow java away. Also, you could certainly be right about what code is generated, but it's probably compiler dependent. When I worked with MSC version 6, it was already known to have optimizations that hadn't existed before. I'm not sure you'll get the same stuff out of other compilers. – Bill K Jun 22 '09 at 21:13
  • Disagree with "clarity". case/break syntax is prone to errors, if a break is omited for instance. A {} block is necessarily closed. Single statement if's with no {} block are generally not recommended because easy to break and difficult to read, I would say that case/breaks are similar. – galinette Apr 07 '14 at 15:05
  • 1
    Maybe is late to discuss that, but I am developing a scripting engine in c++ where in the begin the main operation was switch-case in order to execute each instruction operation of compiled code. I tested a code with simple loop of 100000000 iterations and when I replaced switch-else by if-else I got an optimization of x1.3. The compiler I use is gcc version 5.3, so is an updated compiler so and I can say that swicth cannot be faster than if-else (at least in this compiler). Conclusion: If performance is not critical use switch-case in order to get a clean code. – Jordi Espada Apr 13 '17 at 09:00
  • 1
    @JordiEspada `I can say that swicth cannot be faster than if-else (at least in this compiler)` - no, all you can say `in my particular use case with particular compiler on particular platform if-else was faster the switch`, although it is still hard to believe, perhaps there is something in your code. – qrdl Apr 13 '17 at 09:06
  • @qrdl I cannot confirm nothing but maybe it happens because I'm evaluating the "switch" keyword with big "case-body" each, so the compiler may finds difficult to build an optimized jmp table (but this is a guess of course). Anyway, the optimization was proven. I cannot say that I could see a clear optimization for an small "case-body". – Jordi Espada Apr 13 '17 at 17:03
  • Statement `in worst case switch is as fast as if/else, but typically faster` (p.2 of answer) isn't true for TI C6000 C Compiler, even being invoked with -O3. I found it with version 8.3.2 and had to rewrite switch statements with equivalent if/else to improve performance (nearly by x1.3 as in @Jordi Espada case). – Artem Pisarenko Jan 05 '20 at 15:04
  • And it seems like it's a pretty common pitfall in embedded world: https://embeddedgurus.com/stack-overflow/2010/04/efficient-c-tip-12-be-wary-of-switch-statements/ – Artem Pisarenko Jan 05 '20 at 15:40
32

Well, one reason is clarity....

if you have a switch/case, then the expression can't change.... i.e.

switch (foo[bar][baz]) {
case 'a':
    ...
    break;
case 'b': 
    ...
    break;
}

whereas with if/else, if you write by mistake (or intent):

if (foo[bar][baz] == 'a') {
    ....
}
else if (foo[bar][baz+1] == 'b') {
    ....
}

people reading your code will wonder "were the foo expressions supposed to be the same", or "why are they different"?

Eric H.
  • 2,566
  • 2
  • 23
  • 34
17

please remember that case/select provides additional flexibility:

  • condition is evaluated once
  • is flexible enough to build things like the Duff's device
  • fallthrough (aka case without break)

as well as it executes much faster (via jump/lookup table) * historically

dfa
  • 114,442
  • 31
  • 189
  • 228
  • Are you assuming that compiler designers can't figure out how to convert a series of if/elses into a jump table, or have you actually checked this with a compiler from the last 10 years? – Bill K Jun 22 '09 at 17:52
  • you are right, of course. Let me explain that storically switch is far much faster than if/else, right? – dfa Jun 22 '09 at 17:59
  • Any compiler worth their salt will evaluate the condition only once in an if/else chain. And it will generate an similar jump table. (Provided you've coded the if/else chain much like the switch/case, e.g. not altering the condition etc,) – nos Jun 22 '09 at 18:12
  • 6
    what about if the condition contains a side effect? – dfa Jun 22 '09 at 20:15
  • 3
    If it can't be done as a switch statement, it might only optimize the ones that can. Typically the compiler is much smarter than programmers give it credit for. – Bill K Jun 22 '09 at 21:10
  • @BillK https://stackoverflow.com/questions/1028437/why-switch-case-and-not-if-else-if#comment838651_1028463 – endolith Sep 21 '21 at 20:32
13

Also remember that switch statements allows the flow of control to continue, which allows you to nicely combine conditions while allowing you to add additional code for certain conditions, such as in the following piece of code:

switch (dayOfWeek)
{
    case MONDAY:
        garfieldUnhappy = true;
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
       weekDay = true;
       break;
    case SATURDAY:
       weekendJustStarted = true;
    case SUNDAY:
       weekendDay = true;
       break;
}

Using if/else statements here instead would not be anywhere as nice.

if (dayOfWeek == MONDAY)
{
    garfieldUnhappy = true;
}
if (dayOfWeek == SATURDAY)
{
    weekendJustStarted = true;
}
if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY
    || dayOfWeek == THURSDAY || dayOfWeek == FRIDAY)
{
    weekDay = true;
}
else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY)
{
    weekendDay = true;
}
Joseph Paterson
  • 1,443
  • 4
  • 18
  • 23
10

If there are lots of cases, the switch statement seems cleaner.

It's also nice when you have multiple values for which you want the same behavior - just using multiple "case" statements that fall through to a single implementation is much easier to read than a if( this || that || someotherthing || ... )

Eric Petroelje
  • 59,820
  • 9
  • 127
  • 177
7

It might also depend on your language -- For example, some languages switch only works with numeric types, so it saves you some typing when you're working with an enumerated value, numeric constants... etc...

If (day == DAYOFWEEK_MONDAY) {
    //...
}
else if (day == DAYOFWEEK_TUESDAY) {
    //...
}
//etc...

Or slightly easier to read...

switch (day) {
    case DAYOFWEEK_MONDAY :
        //...
    case DAYOFWEEK_TUESDAY :
        //...
    //etc...
}
hugoware
  • 35,731
  • 24
  • 60
  • 70
4

Switch/case is usually optimized more efficiently than if/else if/else, but is occasionally (depending on language and compiler) translated to simple if/else if/else statements.

I personally think switch statements makes code more readable than a bunch of if statements; provided that you follow a few simple rules. Rules you should probably follow even for your if/else if/else situations, but that's again my opinion.

Those rules:

  • Never, ever, have more than one line on your switch block. Call a method or function and do your work there.
  • Always check for break/ case fallthrough.
  • Bubble up exceptions.
Randolpho
  • 55,384
  • 17
  • 145
  • 179
4

Clarity. As I said here, a clue that else if is problematic is

the frequency with which ELSE IF is used in a far more constrained way than is allowed by the syntax. It is a sledgehammer of flexibility, permitting entirely unrelated conditions to be tested. But it is routinely used to swat the flies of CASE, comparing the same expression with alternate values...

This reduces the readability of the code. Since the structure permits a universe of conditional complexity, the reader needs to keep more possibilities in mind when parsing ELSE IF than when parsing CASE.

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
4

Actually a switch statement implies that you are working off of something that is more or less an enum which gives you an instant clue what's going on.

That said, a switch on an enum in any OO language could probably be coded better--and a series of if/else's on the same "enum" style value would be at least as bad and even worse at conveying meaning.

Bill K
  • 62,186
  • 18
  • 105
  • 157
3

addressing the concern that everything inside the switch has equivalent scope, you can always throw your case logic into another { } block, like so ..

switch( thing ) {
    case ONETHING: {
        int x; // local to the case!
        ...
        }
        break;
    case ANOTHERTHING: {
        int x; // a different x than the other one
        }
        break;
}

.. now I'm not saying that's pretty. Just putting it out there as something that's possible if you absolutely have to isolate something in one case from another.

one other thought on the scope issue - it seems like a good practice to only put one switch inside a function, and not a lot else. Under those circumstances, variable scope isn't as much of a concern, since that way you're generally only dealing with one case of execution on any given invocation of the function.

ok, one last thought on switches: if a function contains more than a couple of switches, it's probably time to refactor your code. If a function contains nested switches, it's probably a clue to rethink your design a bit =)

JustJeff
  • 12,640
  • 5
  • 49
  • 63
1

switch case is mainly used to have the choice to made in the programming .This is not related the conditional statement as :

if your program only require the choice to make then why you use the if/else block and increase the programming effort plus it reduce the execution speed of the program .

sandy101
  • 3,395
  • 3
  • 23
  • 19
0

Pretty sure they compile to the same things as if/else if, but I find the switch/case easier to read when there are more than 2 or 3 elses.

Bill
  • 4,425
  • 3
  • 21
  • 22
  • 1
    Which select are you talking about? There is no such thing in C. – qrdl Jun 22 '09 at 17:31
  • I think the original question was phrased in terms of Select/Case and bill just didn't correct him. +1 compensation point. (by the way, that nets you 8 points, not bad for an error) – Bill K Jun 22 '09 at 17:50
  • Sorry, I am a VB/SQL guy, so Select/Case is the right syntax for me. As for how it compiles, I think I am right for VB6, but I expect VB.Net compiles more like C++. – Bill Jun 22 '09 at 17:56
  • I tried this code, using select/case in C++ and it didn't work. I think select is broken. – Beska Jun 22 '09 at 18:23
  • Since the question (now) refers to switch/case and C/C++, I've edited the answer. Feel free to edit back. – Max Lybbert Jun 25 '09 at 01:02
  • @Bill, Many have discussed above that they might **not** compile to the same thing. `switch`/`case` **may be** implemented by via **jump / lookup table** which then makes it faster. – Md. Abu Nafee Ibna Zahid Nov 25 '17 at 13:54
0

Switch statements can be optimized for speed, but can take up more memory if the case values are spread out over large numbers of values.

if/else are generally slow, as each value needs to be checked.

Erix
  • 7,059
  • 2
  • 35
  • 61
0

A Smalltalker might reject both switch and if-then-else's and might write something like:-

shortToLongDaysMap := Dictionary new.

shortToLongDaysMap
at: 'Mon'     put:  'Monday';
at: 'Tue'     put:  'Tuesday';
at: 'Wed'     put:  'Wednesday'
etc etc.

longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]

This is a trivial example but I hope you can see how this technique scales for large numbers of cases.

Note the second argument to at:IfAbsent: is similar to the default clause of a case statement.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
KHWP
  • 1,211
  • 2
  • 11
  • 17
0

The main reason behind this is Maintainability and readability. Its easy to make code more readable and maintainable with Switch/case statement then if/else. Because you have many if/else then code become so much messy like nest and its very hard to maintain it.

And some how execution time is another reason.

Azhar
  • 20,500
  • 38
  • 146
  • 211