Search

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:

Two

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:

Unknown

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
Index
7.3 -- Common if statement problems

446 comments to 7.4 — Switch statement basics

  • EternalSkid

    Hello Alex and nascardriver, even though i know unimportant, here is just a small error you made.

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

    void printDigitName(int x)
    {
        if (x == 1)
            std::cout << "One" ///////////// You forgot a semicolon
        else if (x == 2)
            std::cout << "Two";
        else if (x == 3)
            std::cout << "Three";
        else
            std::cout << "Unknown";
    }

    int main()
    {
        printDigitName(2);

        return 0;
    }

  • Waldo Lemmer

    Excellent explanation :)

    Just one error:

    Introductory section:
    > The idea behind a switch statements is simple:
    "statements" should be singular

  • Anonymous

    In the first example, there's a semicolon missing at the end of the first true_statement.

  • Hi. Thanks for this material, it's really awesome what you're accomplishing here.

    I didn't understand one thing though Can you help me?

    In the enum class chapter, you mentioned that "With enum classes, the compiler will no longer implicitly convert enumerator values to integers.", but in this chapter there is an example in Case Labels section that points:

    So, if the compiler does not implicitly convert enumerator values to integers, how is this happening in the switch statement? It seems to me that, for it to compare Color::blue with 4 it should be occuring an implict convertion.

    Can you clarify this, please?

    Thank you so much!

  • jessica

    Question #1,case '/' and '%' shall consider the condition when the variable y is zero. It will cause an error.

  • Alex

    Here's my program for question #1:

    If the user enters an invalid operator the program will print (example):

    Enter the first integer: 2
    Enter the mathematical operation: !
    Enter the second integer: 5
    Error. ! is not a mathematical operation.
    2!5 is equal to: 0

    I then included <cstdlib> and put std::exit(0) after the std::cout statement in the default switch case (replacing the statement "return 0;"), to get rid of the unnecessary last printed line.
    I know this is not "best practice".

    Also your solution prints that unnecessary line and I think this is not what we want.

  • yeokaiwei

    For Quiz 2:
    I didn't use getAnimalName() but I managed to get the result.

    Is this acceptable?

    May I ask why we want to use getAnimalName()? It's very confusing to write another function.

    Is getAnimalName() to be combined with other structs?

    • If the final result is what was expected, it's probably acceptable. But you should think more about "is it the best way?".

      This is a simple example, so I agree with you that the function getAnimalName() is not worth it.
      But think broadly, as each function has a goal: getAnimalName is supposed to retrieve the animal name, and printNumberOfLegs is supposed to print the number of legs of the animal. They have different goals.

      Imagine you have another functions: one for type of food each animal eat, one for determining its family (mammal or bird), one to determine if it gives milk, one to determine if you can eat it.

      You would have to type the animals names in all of them, which could take more time to type. If you use the function, the IDE has the option for auto complete, which will save a lot of time when writing huge codes.

      So, think of these examples as a way to show you how things work, but try to expand them to a more complex solution. Then you can evaluate if your answer is "acceptable".

      • yeokaiwei

        I don't know if it's the best way, no one tells us why.

        There are a few problems.

        1. Without a Use case, we do not know what it is for.
        2. The methodology is inconsistent. You learn 1 method in Chapter 3, then in Chapter 4, the Chapter 3 method is redundant. E.g. Lambda calculus, for-range loops. Even in the above Quiz, is it worth remembering?

  • yeokaiwei

    Based on my understanding, I wrote it in one function.

    Is this acceptable for Q1?

  • TonyCheeze

    So in the section about why we can use the variables declared inside a case statement, "Variable declaration and initialization inside case statements", you say that local variables are "created" at the beginning of a block, but can not be seen (scope) until the point of declaration. However, a few sections back when you introduced to topic of automatic storage duration you made the case that local variables have automatic storage duration in that they are created at definition and destroyed at the end of their respective block. But, now you make the case that they are created at the start of a block, not necessarily at the point of declaration. I'm just a little confused, which is it? Obviously I'm missing something here.
    Do you mean that a local variable that is declared but not initialized is created at the start of a block? As opposed to a variable that is declared and initialized that is created at definition? Thanks for any help!

    • nascardriver

      That paragraph went beyond the scope of this tutorial, I've adjusted it.

      Storage for local variables with automatic storage duration is created before or when execution reaches the variable's declaration. When the storage is actually created is not specified (It happens before the variable is first used).

      From a language-point-of-view, variables with static storage duration are usable after their declaration and are initialized when their declaration is executed.

      Even though the previous lesson isn't entirely correct about this, I don't want to adjust it, because getting into low-level details would be more confusing than changing the description of variables with automatic storage duration here.

  • Andreas Krug

    Please take this lesson on a higher beauty level by placing the "Warning: Forgetting the break statement at the end of the case statements is one of the most common C++ mistakes made!" line in a pretty Warning box.

  • newbie

    Hi! Thanks so much for all the tutorials!
    What reason to not do it this way?

  • ARNO

    Hello,
    In a quiz thread question 1 you made a mistake.

        std::cout << "Enter a mathematical operator (+, -, *, /, or %): ";
        char operation{}
        std::cin >> operation;

    the char operation without ; so, we can't compile the programm
    to fix it we need to add ;

    Have a nice day

  • litaci

    how can i improve this program?

  • AE35_Unit

    Great chapters. A step up from the normal tutorials (about the same subjects as these chapters), we get on the web and youtube, which is what I'm looking for. I like the application of what we have previously learned. Notes in code with some slight additions to test ideas form prior chapters. Code below, cheers.

    • nascardriver

      An auto-formatter fixes the indentation for you.
      If multiple cases in a switch do the same, you can let them fall-through

      • AE35_Unit

        Thanks. I've seen the solution and never thought about the fall through, but thought it was pretty cool. I copied it so I can experiment with using code the way the solution had it.  I liked how there was only one call in main to print the outcome so I'm going work with mine to try and get the same "format", thanks.

  • question #1

    Hi,
    Can the following be a good solution for the question #1?

  • sami

    "However, initialization of variables directly underneath a case label is disallowed and will cause a compile error. "

    I think there is an exception that if the case label is the last one (no 'default') we can both declare and initialize a variable.

  • sami

    What value should be passed to the function as an argument to cause 'default label' to be executed? I mean color is of enum class type. How come we can give another value when it is not defined inside enum class Color? Like if it was given the argument 'Color:pink", which doesn't exist, we would get compile error!

    • nascardriver

      An enum is a fancy integer. Every value that is valid for the underlying type (usually `int`), is a valid value for for an enum type.

      There is no enumerator to represent 12345, but it's a valid value.

  • Eric

    I didn't want to write two switch functions, so:

    #include <iostream>

    enum class Animal
    {
        pig,
        chicken,
        goat,
        cat,
        dog,
        ostrich
    };

    struct animalData
    {
        Animal species{ Animal::ostrich };
        int numLegs{ 0 };
        std::string name{ "noname" };

    };

    animalData setAnimalData(Animal animal)
    {
        animalData thisAnimal;

        switch( animal )
        {

        case Animal::pig:
            thisAnimal.species = animal;
            thisAnimal.numLegs = 4;
            thisAnimal.name = "Pig";
            return thisAnimal;
            break;

        case Animal::chicken:
            thisAnimal.numLegs = 2;
            thisAnimal.name = "Chicken";
            return thisAnimal;
            break;

        case Animal::goat:
            thisAnimal.numLegs = 4;
            thisAnimal.name = "Goat";
            return thisAnimal;
            break;

        case Animal::cat:
            thisAnimal.numLegs = 4;
            thisAnimal.name = "Cat";
            return thisAnimal;
            break;

        case Animal::dog:
            thisAnimal.numLegs = 4;
            thisAnimal.name = "Dog";
            return thisAnimal;
            break;

        case Animal::ostrich:
            thisAnimal.numLegs = 2;
            thisAnimal.name = "Ostrich";
            return thisAnimal;
            break;

        default:
            thisAnimal.numLegs = 0;
            thisAnimal.name = "Undefined";
            return thisAnimal;
            break;
        }
    }

    int main()
    {
        animalData newAnimal1{ setAnimalData(Animal::cat) };
        animalData newAnimal2{ setAnimalData(Animal::chicken)};

        std::cout << "A " << newAnimal1.name << " has " << newAnimal1.numLegs << " legs\n";
        std::cout << "A " << newAnimal2.name << " has " << newAnimal2.numLegs << " legs\n";

        return 0;
    }

    Thanks!!

    • nascardriver

      Now you can't do one or the other, you have to do both. If you know that in your program you'll always need the name and number of legs at the same time, that's fine.
      `return thisAnimal` is unconditional, it shouldn't be in the `switch`-statement, same for the species. You also don't need to `break` after a `return`, a `return` stops everything. Otherwise good job :)

      PS: Please use code tags.

  • Córdoba

    As an academic exercise, any improvements. Is this how one would go about using an enum (with user input) to achieve something like this? I realise it's unnecessary to have used enum class for the operator having now seen your solution.  

    Thank you in advance, this website is a god-send.

    • nascardriver

      `getUserOperator` should return the `Operation` right away, not an `int`. You also don't need `selected`. Execution inside a function stops when you `return`.

      • Córdoba

        How do I make it such that if there is an error, it'll go back and give the user another chance to input the operator? And with "getUserOperator", how do you return the operator straight away as it's in an enum Class?

        I get that the easy way is to just return a char as per the solution, just want to understand these enums better

  • Assignment help globally at all level academic. We work 24*7 for your convenience, so you don’t need to wait for any certain time, you can reach any time at our site, for any Assignment Help directly get into touch with our expert write college homework for me, you can concern directly with our Assignment Expert. UK assignment help websites in uk me at any time with good service hire essay helper online.

  • Knight

    How Color::blue evaluates to 4?

    • nascardriver

      Enumerators are integers in disguise. They're numbered starting at 0 and counting up.

  • AndrewPanini

    why is this not working anyone know?

    • Innervate

      The debugger should help you fix these problems.

      I made a few changes and added comments where it went wrong, I think the main issue was a missing brace, which made the main function contained inside of the calculate function. Aside from that, you should use breaks inside of the switch function. Try and make your formatting more consistent using '\n' for readability. Hope this helps!

  • Raffaello

    my take on question #2, should be improved somewhere?

    • nascardriver

      `printNumberOfLegs` doesn't do what it's supposed to do. We can do things like

      That's a very weird cat.

      The signature should be

      Give that a try, you're not too far off.

      As a note to your current `printNumberOfLegs`: Line 41 and 45 are identical. You can let the `case 2` fall through to `case 4`.

  • Gabe

    Quiz 1

    Quiz 2

  • Taras

    I think I got the lesson understanding in general but still confused of declaration and initialization. Please clarify me why this code not working (((

    • nascardriver

      Correct.
      If we use a variable with an initializer, we're safe to assume that the variable has been initialized.
      If line 7 was legal, there'd be a variable with an initializer that has not been initialized.

  • Ambareesh

    Why are use using break statements after "default" in the examples? Isn't that implicit - provided default is added at the last?

  • Dudz

    My answer for question #2

  • Dudz

  • Al

    I've been reading comments, and I want to make sure I did understand why an initialization is not permitted inside a switch statement.

    Initialization asks for two simultaneous things that, in this context, have different degree of contingency: declaration must happen, but you cannot ascertain the assignment will be performed since the execution might never get to that statement, so the engagement of having to do both simultaneously is like saying to the compiler "the program might need to do this and at the same time it should not". Nevertheless, separating declaration from assignment avoids this conflict: the assignment might never happen, but that's OK since thanks to its own statement the declaration will happen anyways.

  • salah

    here Control flow jumps from switch line to the first case line
    so how we could we define a without decleration since compiler will never see int a ;

Leave a Comment

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