7.4 — Switch statement basics

Although it is possible to chain many if-else statements together, this is both difficult to read and inefficient. Consider the following program:

While this example isn’t too complex, x is evaluated three times, and the reader has to be sure that it is x being evaluated each time (not some other variable).

Because testing a variable or expression for equality against a set of different values is common, C++ provides an alternative conditional statement called a switch statement that is specialized for this purpose. Here is the same program as above using a switch:

The idea behind a switch statement is simple: an expression (sometimes called the condition) is evaluated to produce a value. If the expression’s value is equal to the value after any of the case labels, the statements after the matching case label are executed. If no matching value can be found and a default label exists, the statements after the default label are executed instead.

Compared to the original if statement, the switch statement has the advantage of only evaluating the expression once (making it more efficient), and the switch statement also makes it clearer to the reader that it is the same expression being tested for equality in each case.

Best practice

Prefer switch statements over if-else chains when there is a choice.

Let’s examine each of these concepts in more detail.

Starting a switch

We start a switch statement by using the switch keyword, followed by parenthesis with the conditional expression that we would like to evaluate inside. Often the expression is just a single variable, but it can be any valid expression.

The one restriction is that the condition must evaluate to an integral type (see lesson 4.1 -- Introduction to fundamental data types if you need a reminder which fundamental types are considered integral types). Non-fundamental types that are convertible to an integer (e.g. enumerated types and some classes) are also valid. Expressions that evaluate to floating point types, strings, and other non-integral types may not be used here.

Following the conditional expression, we declare a block. Inside the block, we use labels to define all of the values we want to test for equality. There are two kinds of labels.

Case labels

The first kind of label is the case label, which is declared using the case keyword and followed by a constant expression. The constant expression must either match the type of the condition or must be convertible to that type.

If the value of the conditional expression equals the expression after a case label, execution begins at the first statement after that case label and then continues sequentially.

Here’s an example of the condition matching a case label:

This code prints:


In the above program, x is evaluated to produce value 2. Because there is a case label with value 2, execution jumps to the statement underneath that matching case label. The program prints Two, and then the return statement is exited, which returns back to the caller.

There is no practical limit to the number of case labels you can have, but all case labels in a switch must be unique. That is, you can not do this:

The default label

The second kind of label is the default label (often called the default case), which is declared using the default keyword. If the conditional expression does not match any case label and a default label exists, execution begins at the first statement after the default label.

Here’s an example of the condition matching the default label:

This code prints:


The default label is optional, and there can only be one default label per switch statement. By convention, the default case is placed last in the switch block.

Best practice

Place the default case last in the switch block.

Taking a break

In the above examples, we used return statements to stop execution of the statements after our labels. However, this also exits the entire function.

A break statement (declared using the break keyword) tells the compiler that we are done executing statements within the switch, and that execution should continue with the statement after the end of the switch block. This allows us to exit a switch statement without exiting the entire function.

Here’s a slightly modified example rewritten using break instead of return:

The above example prints:

Three Ah-Ah-Ah!

Best practice

Each set of statements underneath a label should end in a break statement or a return statement.

So what happens if you don’t end a set of statements under a label with a break or return? We’ll explore that topic, and others, in the next lesson.

7.5 -- Switch fallthrough and scoping
7.3 -- Common if statement problems

446 comments to 7.4 — Switch statement basics

  • Dimbo1911

    Does this seem ok?

  • GFK

    "If this seems a bit inconsistent, it is."

    Well, that's how C was supposed to work. Somehow...

  • Paulo Filipe

    Hello, I just need a clarification:

    If I have a switch statement inside a function, and if a case has a return statement, do I still need the break statement after it or the return stops the code execution on that switch / function?

  • Arthur

    I worked at this I seem to have corrected the errors and gave it good error correction is it an ok way to use fall through?

    • Hi Arthur!

      * Line 74: Initialize your variables with brace initializers. You used copy initialization.
      * Line 10, 29: Initialize your variables with brace initializers.
      * Line 72: Unnecessary first comparison. Use a do-while-loop.
      * Don't use abbreviations unless they're obvious.
      * Line 11-17 and 18-24 are mostly equivalent. Use a function.
      * Line 40, 42 ff: Duplicate code.
      * @calculate should return a bool.

      • Arthur

        hi nascardriver , thanks for the reply , I had actually changed some of my original prior to revisiting this post none the less your comment is valuable and helps. I changed things from the quiz specs going to doubles instead of int's , I had already removed 'getfnctn' , and now hopefully addressed your observations I had already renamed a lot of variables are the abbreviations reasonable? am also wondering if the formatting is reasonable?

        • * Line 11: Initialize your variables with brace initializers.
          * Line 18, 22: You can't compare two values at the same time.
          * Line 55, 60: Booleans are false/true.
          * Duplicate code in line 34-54.

          > are the abbreviations reasonable?
          No, write them out. If you wouldn't use the abbreviation when talking, don't use it when coding.

          > formatting is reasonable?
          As long as your formatting is consistent and readable, it's up to you how you format. Line 19-20 should be wrapped in curly brackets. Your formatting is inconsistent and you've shown that you're having trouble doing it manually, so use your editor's auto-formatting feature.

          • Arthur

            thanks again nascardriver , I know formatting is a personal choice and that mine is inconstant still , probably should ask if it is readable :) been using code::blocks on default settings. A lot of changes again, still stops invalid function entry and I narrowed the check on zero down to only block division by zero. I also tried to space things so it is more readable.

            • Arthur

              oh I guess I need an add in between 85 & 86 to make it work right

              guess I didn't check all variations of secondNumber = 0  whoops!

            • * Line 13: Initialize your variables with brace initializers. You used copy initialization.
              * Line 42: Don't compare booleans to false/true.
              * Line 32: @checkFunction is always false at this point.
              * Line 79: Use double literals when calculating with doubles.

              You're still not using auto-format (Line 94, 95).
              I don't like that broken lines continue with the same indention as their predecessor.

              • Arthur

                ok thank you

                line 13 , get rid of '='
                getFunction: flip all the true and false 's and have line 42 simply  while (checkFunction) which I see the logic in that.

                line 79 if (secondNumber == 0.0)

                line 94 ,95 : I see what you mean , I am trying to follow code::blocks formatting suggestion but I actually typed that in one line then broke it down and missed that code::blocks had suggested two space indent , it does look better with the indent and I figured out how I lost the suggested formatting there so will watch for that.

                on line 32 I am not sure what you mean "@checkFunction is always false at this point." I've tested all functions by running it and they all work?

                I added in a try again function nesting do while statements  in 'int main' , plus I was getting a warning @ 42-43 because of the lack of a return statement , I put that in even though it is useless, not really sure if that is the thing to do but seems harmless. figure I spent about enough time on this one , I figure I learned some  , thanks again.

                • > @checkFunction
                  @checkFunction is only set to true in line 24-36. Every time it is set to true, the function returns.
                  There's no way for @checkFunction to be true in line 42 (I meant 42 before, must've been a typo, sorry).

                  > I put that [return] in even though it is useless
                  So you agree that @checkFunction is never true in line 42. If you don't want a loop to stop, use while(true).

                  • Arthur

                    oh I see, I have a bunch of statements to change true/false when it never actually needs to change because the escape from the loop is the return statement , and a bunch of extra return statements because I needed to go back and reread "case labels, multiple cases and, default label"

                    thanks again, very helpful.

  • J


    Here's what I came up with for Quiz 1. Any tips?

  • I've always hated the "fall-through" thing in C and its variants (as I've also suffered in JavaScript and php because of it). I guess Go had a nice approach to make "break" no longer needed and to create the keyword "fallthrough" in the rare cases it's really needed. Some languages do not even support it (I guess for obvious reasons) and C# has the strangest approach I ever encountered... Forgetting "break" or "return" will throw a compiler error and if you wanna "fallthrough" you gotta use "goto case <nextcase>;" which looks like completely dirty code to me....

    Does C++ throw a warning for forgetting "break" statement in 'switch' or is that a setting? XD

    • C++ doesn't throw any warnings, the compiler does.
      But yes, you get warnings for fall-through cases. The correct way of handling this is not by disabling the warning, but by using an attribute on that case to let the compiler know that you're falling through on purpose:

      I don't think clang++ enables -Wimplicit-fallthrough (The warning about fall throughs) with any warning group, so you probably won't see warnings even without the attribute.
      It's good practice to mark the fall-through cases anyway.

  • Fatih Türkmen

    Hi Alex, I appreciate you for tutorials, thank you so much :)
    In the first example's solution when calculate function returned 0 from default case wouldn't there be a printing error?
    I think it would print both main and default case's std::cout
    sorry for my bad english :(

    • Alex

      Yes. But this is an error case -- so the fact that it has a printing error is less important than making sure the programmer notices the error so they can fix it.

      This would probably be better done as an assert rather than a std::cout (but I haven't covered assert yet in the lesson order).

    • Fatih Türkmen

      Oooh i see, thank you for reply

  • Erk_Forever

    After I cleaned up any errors the compiler kicked back at me, I got two new ones : undefined reference to 'WinMain@16' and error: 1d returned 1 exit stats.
       I don't even have the slightest clue what this means. If I make add an error to my code on purpose, these errors vanish from my build messages.
       Any help is appreciated.

  • hassan magaji

    Hy everyone,
    correction || advice || remark; /* please */

    • Hi Hassan!

      * Line 14, 15: Unnecessary forward declarations, move @main below those functions.
      * Line 14, 15: Unnecessary use of trailing return type, the regular syntax is shorter and easier to read.
      * You're using the same name style for variables and functions, this can lead to confusion.
      * @getAnimalName: You're constructing an @std::string with every call to @getAnimalName, but you don't need an @std::string. Use a const char* or @std::string_view. Constructing an @std::string creates a copy of the string literal, which is slow. You'll also need to update line 35 to prevent the construction of a string there. This is @Alex' fault.
      * Storing the number of legs as a character prevents you from doing arithmetic with them. Store the actual numbers.
      * Line 59: Use the "default" label to improve readability when your code is properly formatted. You could also remove line 60 and initialize @numOfLegs to '?'. This won't work once you use numbers.
      * Line 63: Unnecessary comparison of @name to "ostrich", it's enough to compare name[0] to 'o'.

      These aren't big problems, keep it up!

  • C++guy

    I used the std::cin trick we saw on the std::string lesson so I don't have to ask the user three different time to input something and even if he doesn't put a whitespace between his reponses he will still have to put 3 different answers so that's ok. I was wondering when we say something like case '+' the '+' is converted into ASCII code (which is an int) and when the user input + it's converted into ASCII code too right ? I'm asking this because I tried to put case = 93 (93 is ASCII code for + if I remember well) and it worked too.

    • * Initialize your variables with uniform initialization
      * @main is missing a return value

      > the '+' is converted into ASCII code

      > which is an int
      no. it's a char, but a char is an integral type

      > when the user input + it's converted into ASCII code too right

      • C++guy

        Oh ok I understood your point. The last thing I don't get is why you can assign a value to a variable inside a case with no block but not initialize it. They made this rule to follow the if/else rule (if you define something inside an if it's destroyed) and give more possibilities but assigning something is just like iniatilizing no ? I mean I know there's a difference but at the end of the day when you assign or initialize you give a value to some memory location so why isn't it forbidden too ? Is it because an assignment is only made when executed and not initialization ?

        • c can access the variables declared in b, because a switch doesn't use scopes. When c is executed, @i exists, but hasn't been initialized, because b never ran, meaning that @i has an undefined value although it has an initializer. That's why initializing variables inside a switch is illegal. @j is legal, because it doesn't have an initializer. A variables declared with an initializer must be initialized before it can be used.

          • C++guy

            Oh okay thanks a lot I got it now. That's why assigning instead of initialization isn't a problem, if it isn't executed then the variable will just be declared but because it doesn't have an initializer it isn't a problem if it isn't initialized. And when we put a block when initializing if it doesn't execute then the variable has block scope so it's destroyed after the block and there's no problem with no initialization.

  • Outrageous Gem

    Just to confirm:
    The "break" in the last case of a switch statement is not needed as it would always stop at the end of the switch block? You are just including it for good measure?

    • Alex

      Correct. The trailing break is really more of a defensive programming / documentary practice, so that:
      1) we can add new cases to the bottom without having to go back and modify the previous one by adding a break (less chance for error)
      2) it is more clear that the last case ends immediately and should not flow through into any additional cases added later.

  • Nayab Anjum

    Can we use logical operators with switch statements:

  • Nayab Anjum

    When I executed following lines of code I got output '4354190' but I was expecting to get 5

    #include <iostream>

    using namespace std;

    int main()
        switch (3)
        case 1:
            int y; // okay, declaration is allowed within a case
            y = 4; // okay, this is an assignment

        case 2:
            y = 5; // okay, y was declared above, so we can use it here too

        case 3:
            cout << y << endl;

            cout << "default case" << endl;
        return 0;

    • Alex

      When you switch (3), the code jumps immediately to case 3, which prints the value of y. y has been defined (in case 1), but not given a value (since neither the assignment in case 1 or 2 executed), so you're printing the value of an uninitialized variable.

  • DAT

    Question 1:


    struct AnimalDescript
        bool bSuccess;
        std::string animalName;
        int animalLeg;

    enum class Animal

    AnimalDescript animalInformation(Animal animal)
        switch (animal)
        case Animal::PIG:
            return{ true,"pig",4 };
        case Animal::CHICKEN:
            return { true,"chicken",2 };
        case Animal::DOG :
            return { true,"dog",4 };
        case Animal::CAT:
            return { true,"cat",4 };
            std::cout << "Error" << "\n";
            return { false };
    void printAnimalInformation(AnimalDescript x)
        if (x.bSuccess)
            std::cout << " A " << x.animalName << " has " << x.animalLeg << " legs." << "\n";

    int main()
            AnimalDescript x= animalInformation(Animal::CAT);
            AnimalDescript y = animalInformation(Animal::CHICKEN);
            return 0;

  • Chassz

    This works but I first tried to use

    instead of

    to enter the operator symbol into thinking that char was a type of int but this didn't work. Can someone expand on why?

    • Alex

      std::cin handles int and char inputs differently. '+' is a valid char input, but not a valid int input, as int input is assumed to be numeric.

      Once '+' is actually input, then it is represented as an integer -- so the issue is really with how std::cin behaves.

  • Kio

    For Quiz 1.

    Maybe we can input some basic validation.

  • This solution works:

    but when I tried to use enum class (Animal) - I use VS2017 - everything was out of scope in the case statements.  Is this a peculiarity of switch/case blocks?

    • Hi Nigel!

      Line 8: Use enum class
      Line 36: This will cause a crash, return an empty string.
      Line 57, 61: Space before line feed is memory waste.

      The rest looks good!

      • Thank you.

        <Line 8: Use enum class>

        When I do this every case in the switch block is out of scope so not sure how to solve this.

        <Line 57, 61: Space before line feed is memory waste.>
        Thank you - I never considered it a waste of memory but I suppose a space is still another character - I was just trying to make the code easier to read.

        • > not sure how to solve this
          Enum classes declare enumerators in their own namespace to avoid name collisions. Give lesson 4.5a another read, it's short.

          > I never considered it a waste of memory
          Yeah, it's just one byte, but it's still wasteful.

          Less 4.5a - Enum classes

          • Aha! Got it now:

            Thankyou - for helping out on this.  I take it the above solution is now correct?

            • Nope, read your compiler's warnings.
              @getAnimalName doesn't return a value in every case. Add a

              • Sorry!

  • MorbidPitaya


    In task 1 of the Quiz an incorrect mathematical operator has two lines of code output to the console window:

    (number 1), (mathematical operator) and (number 2) represent the user-defined integer values and operator itself.

    In spite of the fact this may have been made intentional, I had been desperate to omit having the second line being printed out. I have found a solution. Simply substituting the line (37)

    with the following code

    does the trick :V

    Doing so results in only the message ("calculate(): Unhandled case") being printed. Does my option seem logical and/or syntactically concise?

    Great tutorials/lessons!

    • Hey!

      > Does my option seem logical

      is always true.
      Either try it with some example values in your head, or apply de morgan's law, which makes it a little more obvious

      The expression inside the brackets is false, because @op can only have one value at a time, negating that gives you true.

      You now have

      But 0 is a valid result of a calculation, you should only care about erroneous operators.

      So, your logic is wrong, and on top of that you're calling @calculate twice, which is a waste, because it will return the same value both times.
      Here's my approach with what you know from the lessons so far:

      You'll learn about better solutions later on.

      • MorbidPitaya


        Huh... That (code in your answer) was quite intuitive! Thank you very much for answering!

        Guess I should have thought it through thoroughly and tested with a broader range of examples.

        Could you also tell me what meaning line 44 (in your code) holds? I do not seem to remember using '\x00' as a value to initialise a char variable.

        Thanking you again for the quick reply!

  • Nguyen

    Hi Alex & nascardriver,

    "... Because the statements under each case are not inside an implicit block, that means all statements inside the switch are part of the same scope. Thus, a variable defined in one case can be used in another case, even if the case in which the variable is defined is never executed!"

    After reading the paragraph, I decided to write a small program for testing.  

    #include <iostream>
    using namespace std;

    int main()
        cout<<"Enter 1 or 2: ";
        int x;
    switch (x)
        case 1:
            int y;
            y = 4;
        case 2:
           cout << "default case" <<endl;
         return 0;
    The output:

    Enter 1 or 2: 2

    I really expected 4 instead of 0.  Please review my program and let me know if there is something wrong?

    Thanks, Have a great day.

    • Hi Nguyen!

      Please use code tags.

      You entered 2, case 1 is skipped, execution starts at

      @y is accessible, but has not been initialized nor has the code in case 1 been executed, you get an undefined value, which happens to be 0.

      • Nguyen

        Thank you nascardriver,  

        This means a variable defined, not its assignment, in one case can be used in another case, even if the case in which the variable is defined is never executed!  

        It’s a little less weird, something I should keep in mind.

        Thanks again.

  • Jörg

    hello, is this valid aswell? thanks for the tutorials btw <3333

  • seriouslysupersonic

    Hi, I am curious as to why the code below compiles and runs just fine (using g++ 7.3.0), since I am initializing b inside the last case.

    If I had an extra case, I get the "crosses initialization" error; however, initializing a variable inside the last case doesn't generate a compiler error.

    • Hi there!

      The problem with initialization in cases is that the case could fall through and the variable could be initialized again in a later case (but variables cannot be initialized multiple times). (EDIT: I think that's wrong, but the real reason is similar).
      Since case(2) is your last case, there is no other case behind it, and a fall-through wouldn't change anything, so you're allowed to initialize variables in the last case.
      Even though this doesn't cause an error, I suggest you to write cases in a way that allows them to be position independent (Allow them to be move above and below any other case without changing the behavior), eg. by scoping the cases.

      • seriouslysupersonic

        Hi nascardriver!

        Thanks for the quick answer. However I am still a bit confused as to what is happening behind the scenes when the program executes.

        If the user enters 1, is variable b just created an left uninitialized or is it also initialized to 2 at the start of the block, even though the initialization statement in case (2) is never reached?

        • @b only exists when line 16 has been reached. When that happens, it is also initialization. case(1) doesn't know about @b.
          The rule is, when a variable in a case has an initializer, it must be inaccessible unless initialization is guaranteed.
          Since @b is only accessible after line 16, an it is initialized in line 16, there is no problem. If @a had an initializer and @input is 2, @a would be accessible in case(2), but @a would not have been initialized, which is illegal. Declarations without initializers are allowed. As it is, @a can be used in case(2).

  • Tamara

    Hi, I'm having trouble understanding as to why it's illegal to initialize a variable directly underneath a case statement, but it's legal to initialize it if it's inside a block?

    • Alex

      The C++ standard says, "It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer."

      Basically, that means it's illegal to jump over a local variable that has an initializer to a point where that variable is still in scope.

      Putting x inside a block ensures x will be destroyed before the next case, ensuring that the switch can't jump the user to a point where x is still in scope. Thus, x can be safely initialized in this case.

  • Idda

        I'm facing problem in understanding why is it not allowed to intialize a variable just before a case lable?

  • Jack

    If you are returning a value inside a case label, is there any need to break as there is no chance of fall-through? I know the break won't be executed, but is it good practice to keep it there? ie:

    Poor example, but just to illustrate the point. Some languages complain about an unreachable statement (I've seen this error in Java a few times when you return and then write more statements under it), is this true for some versions of C++?

    • nascardriver

      Hi Jack!

      The break after a return will never be reached and adding a break won't change anything.
      I still like having the break there just for good measures.

  • Henry

    Hi Alex

    Your solution is good but it has a bug
    To reproduce, try to use an animal that is not defined and the error message will come out all weird.
    e.g: printNumberOfLegs(Animal::TIGER);

    I think my solution you should not break the cout message
    So do something like that:
    void printNumberOfLegs(Animal a) {
      using namespace std;

      switch (a) {
        case Animal::CHICKEN:
        case Animal::OSTRICH:
          cout << "A " << getAnimalName(a) << " has 2 legs.\n";
        case Animal::GOAT:
        case Animal::CAT:
        case Animal::DOG:
        case Animal::PIG:
          cout << "A " << getAnimalName(a) << " has 4 legs.\n"; break;
        default : cout << "ERROR: undefined animal!\n";

    You can break the last part of the message but then you need to use return and not break.

    • Alex

      Example updated to use "???" as the number of legs instead of the more descriptive error message. Either way is fine, as this message should only come up as a defect.

  • ArmaIG

    Hi Teachers, when I enter anything else that isn't a number when the program is asking for an integer, the programs executes the rest incorrectly, is there anyway to deal with that problem with the tools we know so far?

    • nascardriver

      Hi ArmaIG!

      This is covered in lesson 5.10.
      Now that you know of switch-statements, those are even better than an if-else-if in your monster to string function.

      * Lesson 5.10 - std::cin, extraction, and dealing with invalid text input

  • Dayyan218

    I wanted to ask, when is it a good time to use switch statements instead of if statements?
    To me switch statements aren't as easy to read as if-else statements. What are the pros of using switch statements over if-else statements?

    • nascardriver

      Hi Dayyan!

      "Use 'switch' when you have a limited set of values that you want to test against, eg. enums or characters.
      Use 'if' otherwise."

      > What are the pros of using switch statements over if-else statements?
      It's easier for your compiler to optimize switch-statements than if-else-statements.

  • Rohit

    You mention that variables can't be initialized inside a case "because initializing a variable does require execution, and the case statement containing the initialization may not be executed!"

    So I'm curious as to why it's illegal to initialize before all of the case labels? Because all the statements before the first case are executed anyway right?

    This gives the error code C2360 "initialization of 'x' is skipped by 'case' label" in VS 2017. I'm wondering what's going on.
    I tried to put 'x' in its own block and declare it as a static int to try to access it outside of the block scope but that doesn't work. I had forgotten that static variables are only accessible in the block they're declared. Regardless, the initial issue of it not letting me initialize there still occurred. (edit: scratch that, it let me initialize in its own block. but if i can't access it after the block then it's useless anyway.)

    I can always declare on one line and assign on the next but I was just curious as to why this happens.

  • Samira Ferdi

    Hi nascardriver! Thank you for reply!
    So, when the best practice to use switch-case rather than if-else and otherwise?

    • nascardriver

      Hi Samira!

      Use 'switch' when you have a limited set of values that you want to test against, eg. enums or characters.
      Use 'if' otherwise.
      That's how I do it.

  • Samira Ferdi

    So, the only difference betwen if-else chain and switch-case is the switch-case is more easier to read than if-else chain?
    When best practice to use if-else rather than switch-case and otherwise?

    • nascardriver

      Hi Samira!

      No, there are more differences.

      * You can only switch one value whereas if-elseif takes any boolean expression.
      * "cases will overflow into subsequent cases", 'if' doesn't.
      * 'switch' behaves differently when using blocks vs when not using blocks: "You can declare (but not initialize) variables inside the switch", 'if' can initialize.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]