Switch versus if-else if



  • I've been having a pretty heated debate on another forum regarding this. I'm not sure what the actual disagreement is anymore, but i believe their point was switches are shorthand for cascading if else if else blocks, and i said that they were most certainly handled differently.

    I've been told by a c/c++ guru that switch (expression) - expression only gets evaluated once. and for if/else if each line gets read unless some expression evaluates to true. Now i know that the if/else if one is valid. What i am wondering is this, since you guys have probably tweaked compilers for fun:

    Under what circumstances would a switch perform better (barring fallthrough, that was the other argument)?  and then under which similar circumstances would if/else if perform better?

    Assume whatever you wish, i kinda want to see this hashed out. I'll toss in my two cents here and there, but be aware i am not even approaching a competent programmer. It took me about an hour to code a non-trivial switch statement that used fallthrough elegantly. Of course half of that was trying to remember c++ symantics, but whatever :-)

     Any takers?



  • From what I've heard, a switch is compiled to pretty much exactly the same as a functionally equivalent if-else block. It's still just a series of conditional jumps.

    It might be fun to use the switch value as the index of a frame pointer array though. I used function pointers in that manner to write a program that counts to 100 without any conditionals. It's somewhere in the Coding Challenge subforum.



  • i did that challenge as well. I'd heard that switch statements, when written properly utilize a jump table (elaborate goto lookups or something, i'm going on 36 hours here) - and that if-elif blocks do not.

    Here's an interesting aside:

    visual studio c++ allows fall through case statements. VB.net does not, by default. then again, neither does C#, but you can force it to jump to another case statement - but the breaks are implied.

    Just little things i discovered last night playing with fall throughs. :-) 



  • @GeneWitch said:

    visual studio c++ allows fall through case statements. VB.net does not, by default. then again, neither does C#
     

    Did they put that in just to confuse people coming from Java?

     



  • You can also do this:

    switch (foo)
    {
    	while (Foo.ready())
    	{
    case NULL:
    		foo = Foo.getNextFoo();
    default:
    		foo->doSomething();
    	}
    }


  • @GeneWitch said:

    visual studio c++ allows fall through case statements. VB.net does not, by default. then again, neither does C#, but you can force it to jump to another case statement

    I never try to write logic that would depend on a switch fall through. Many problems are caused in C++ by programmers who failed to put a break after a case. It is against my coding sense to ever really want to do anything like that. If you want to evaluate a case multiple times, try making a loop or a smallfinite state machine or something... That way you can just transit to the state that needs to do processing and it will process that state until the proper conditions are met.

    When you back up and look at it, fall through should not be allowed anyway... because it will execute both every time. If you have to do an if statement to force it to only run in certain situations due to a fall through, you just defeated the purpose of the switch and should prolly not have even used it.

     Another thing... forcing via goto or anything like that is against basic software engineering principles. It should be avoided.



  • If you're trying to improve performance by replacing <font face="Courier New">if</font> with <font face="Courier New">switch</font>, you're probably digging the wrong way. Anyway, profiling, profiling and (one more time) profiling.



  • @pitchingchris said:

    I never try to write logic that would depend on a switch fall through. Many problems are caused in C++ by programmers who failed to put a break after a case. It is against my coding sense to ever really want to do anything like that. If you want to evaluate a case multiple times, try making a loop or a smallfinite state machine or something... That way you can just transit to the state that needs to do processing and it will process that state until the proper conditions are met.

    When you back up and look at it, fall through should not be allowed anyway... because it will execute both every time. If you have to do an if statement to force it to only run in certain situations due to a fall through, you just defeated the purpose of the switch and should prolly not have even used it.

     Another thing... forcing via goto or anything like that is against basic software engineering principles. It should be avoided.

     

    This statement seems really short sighted.  A switch/case is a very well understood construct (by people other than you, apparently), and fall through has lots of good uses.  I guess if you find it too confusing, then you shouldn't be using it.  At the risk of channeling asuffield, this seems very much like the blub effect. 

    In any case, my understanding (for C at least, I'd assume that C++ is similar/same) the compiler creates a jump table based on the various cases, and jumps down to wherever specified, so the cases are just labels, not really flow control operators (like an else or a closed bracket).  Hence the need for the break.  One sort of use is where you have been passed an array of data.  You don't know how many elements are valid, as the caller may be relying on default values to be assumed.  So the construct might look like:

     

    switch(params){

        case 4:

    // do stuff 

        case 3:

            // do stuff

        case 2:

            // do stuff

        case 1:

            // do stuff

        case default:

            // do stuff

    }

     

    With some code to transform the data as required after each respective case. 



  • @GeneWitch said:

    and i said that they were most certainly handled differently.
     

    I don't know which one performs better (if at all), but I know I use switch statements regularly for legibility. It's far easier to read through a switch for a long list of conditionals than the equivalent if/else/else/else/else/else... block.

    On occasion, fallthrough behavior does come in handy, particularly where two cases share a significant chunk of code, but one has something extra, then this is far better:

    switch(someval) {
         case 'a':
            minor_difference(); // fall through
        case 'b':
            shared_code();
    }

    than

    if (someval == 'a') {
        minor_difference();
        shared_code();
    } else if (someval == 'b')  {
        shared_code();
    }



  • @boomzilla said:

    In any case, my understanding (for C at least, I'd assume that C++ is similar/same) the compiler creates a jump table based on the various cases, and jumps down to wherever specified, so the cases are just labels, not really flow control operators (like an else or a closed bracket).  Hence the need for the break.  One sort of use is where you have been passed an array of data.  You don't know how many elements are valid, as the caller may be relying on default values to be assumed.  So the construct might look like:

     

    switch(params){

        case 4:

    // do stuff 

        case 3:

            // do stuff

        case 2:

            // do stuff

        case 1:

            // do stuff

        case default:

            // do stuff

    }

     

    With some code to transform the data as required after each respective case. 

     

    Maybe I was wrong. In my experience, if you excluded the break, it will execute both statements, even if the second one didn't really hold true. Almost like if it read     case 4: case3: case 2: case 1:.   

    I ran into this time and again, where if I didn't include the break, it actually ran the code it fell into, even when that case wasn't true.



  • @pitchingchris said:

    I ran into this time and again, where if I didn't include the break, it actually ran the code it fell into, even when that case wasn't true.
     

    Well, that's because that's exactly how a switch is supposed to work. The case statements are entry points for when the switch itself is evaluated. They're not implicit breaks for previous cases. The switch is evaluated once, exececution jumps down to whatever case matches the switch condition, and then continues executing until the end of the switch, or a break is encountered.

    This is about the only way in which a switch differs from the equivalent if/else/else/else/... sequence. You can't "fall through" in an 'if' block.



  • @MarcB said:

    Well, that's because that's exactly how a switch is supposed to work. The case statements are entry points for when the switch itself is evaluated. They're not implicit breaks for previous cases. The switch is evaluated once, exececution jumps down to whatever case matches the switch condition, and then continues executing until the end of the switch, or a break is encountered.

    This is about the only way in which a switch differs from the equivalent if/else/else/else/... sequence. You can't "fall through" in an 'if' block.

     That was the point I was trying to make. It seemed like they were saying it would execute multiple handlers, and only for the ones that the condition was true. 



  • @GeneWitch said:

    visual studio c++ allows fall through case statements. VB.net does not, by default. then again, neither does C#, but you can force it to jump to another case statement - but the breaks are implied.

    VB has never allowed this fall through behaviour.  Probably because the designers of VB thought it would be beyond the understanding of the target audiance :)  I am a VB programmer and have on occasions wished for this ability, but not often...

    My understanding of the performace was that the switch (or select) statement only evaluated the expression once.  Although I believe modern compilers will apply similar optimisations to the if - elseif construct if it can.
    I normally base my decision on which to use on readabilty rather than performance.  However, some of my co-workers favour this

    select case true
    case typeof obj is class1
    ...
    case typeof obj is class2
    ...
    end select

    instead of

    if typeof obj is class1 then
    ...
    elseif typeof obj is class2 then
    ...
    end if

    which I think is going too far.  Somehow it always makes me feel slightly dirty.  What does everyone else think?



  • You really would need to use a profiler to find out for sure, but most c++ compilers have pretty good optimization these days.  So there might not be any differece at all if a compiler can optimize both options to the same machine code.

    It's hard to believe that a switch vs an if/else statement would be a notable bottleneck in your program. 



  • @pitchingchris said:

     That was the point I was trying to make. It seemed like they were saying it would execute multiple handlers, and only for the ones that the condition was true. 

    That's not what I was trying to do or say.  I was demonstrating an instance where fall through makes life easy and legible.



  • @pitchingchris said:

    That was the point I was trying to make. It seemed like they were saying it would execute multiple handlers, and only for the ones that the condition was true. 
     

    I'm confused by your terminology.

    There's the switch's condition up top, which gets executed exactly one (1) time. Its resultant value -- a constant -- is compared to the case labelled values. Depending on your language spec/interpreter, these values may be dynamic, as for example, in Javascript or PHP.

    Fallthrough is a nice feature. Being worried about programmer lax is not a reason to forbid it. Compared to a cumbersome if-else-if chain it has a beautiful, compact, clear syntax. If I encounter an ifelse in other people's code that compares to a constant, I often change it to a switch. It's better.

    I hate ifs anyway. But that's me.



  • I was simply mistaken. I thought they were trying to do something like this:

    switch (someCondition)
    {
     case 4:
       logicForCondition4();
     case 3:
       logicForCondition3();
     case 2:
       logicForCondition2();
     case 1:
       logicForCondition1();
    }

    and it only execute the one where the condition passes. Well thats not the case. if someCondition was 3, than it would execute 3, 2, and 1.  Thats what was intended originally, but I rarely run into a case where I have to do that. I just got mistaken on intent.  I never use fallthrough, but thats just me.



  •  C# has implicit breaks, as does VB.net; as i said i haven't really had the need to test this in other languages...

     

    Thanks for the dialogue so far, by the way, guys (and gals) 



  • @GeneWitch said:

    (and gals) 

    That's quite alright! 



  • The way I understand it, a switch statement that looks like this:

    switch (someintegervariable) {
      case 1:
        doSomethingForCase1();
        //no break, just to show what's happening
      case 3: //note: case 2 is left out, using default instead
        doSomethingForCase3();
        break;
      default:
        doSomethingForAllOtherCases();
        //there is never a break here, it ends anyway
    }

    might be translated into this kind of machine code: 

        GOTO SWITCH_START
    JUMP_TABLE:
        CASE_DEFAULT //index 0 = default
        CASE_1 //index 1 = case 1
        CASE_DEFAULT //etc.
        CASE_3

    SWITCH_START:
        IF someintegervalue < 0 && someintegervalue > 3 GOTO CASE_DEFAULT
        LOAD ADDRESS FROM JUMP_TABLE AT INDEX someintegervalue
        JUMP TO loaded address

    CASE_1:
        CALL DOSOMETHINGFORCASE1
    CASE_2:
        CALL DOSOMETHINGFORCASE3
        GOTO SWITCH_END
    CASE_DEFAULT:
        CALL DOSOMETHINGFORDEFAULTCASE
    SWITCH_END:

     If this is of any help.


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.