Search

7.5 — Switch fallthrough and scoping

This lesson continues our exploration of switch statements that we started in the prior lesson 7.4 -- Switch statement basics. In the prior lesson, we mentioned that each set of statements underneath a label should end in a break statement or a return statement.

In this lesson, we’ll explore why, and talk about some switch scoping issues that sometimes trip up new programmers.

Fallthrough

When a switch expression matches a case label or optional default label, execution begins at the first statement following the matching label. Execution will then continue sequentially until one of the following termination conditions happens:

  1. The end of the switch block is reached.
  2. Another control flow statement (typically a break or return) causes the switch block or function to exit.
  3. Something else interrupts the normal flow of the program (e.g. the OS shuts the program down, the universe implodes, etc…)

Note that the presence of another case label is not one of these terminating conditions -- thus, without a break or return, execution will overflow into subsequent cases.

Here is a program that exhibits this behavior:

This program outputs the following:

2
3
4
5

This is probably not what we wanted! When execution flows from a statement underneath a label into statements underneath a subsequent label, this is called fallthrough.

Warning

Once the statements underneath a case or default label have started executing, they will overflow (fallthrough) into subsequent cases. Break or return statements are typically used to prevent this.

Since fallthrough is rarely desired or intentional, many compilers and code analysis tools will flag fallthrough as a warning.

The [[fallthrough]] attribute

Commenting intentional fallthrough is a common convention to tell other developers that fallthrough is intended. While this works for other developers, the compiler and code analysis tools don’t know how to interpret comments, so it won’t get rid of the warnings.

To help address this, C++17 adds a new attribute called [[fallthrough]].

Attributes are a modern C++ feature that allows the programmer to provide the compiler with some additional data about the code. To specify an attribute, the attribute name is placed between double hard braces. Attributes are not statements -- rather, they can be used almost anywhere where they are contextually relevant.

The [[fallthrough]] attribute modifies a null statement to indicate that fallthrough is intentional (and no warnings should be triggered):

This program prints:

2
3

And it should not generate any warnings about the fallthrough.

Best practice

Use the [[fallthrough]] attribute (along with a null statement) to indicate intentional fallthrough.

Sequential case labels

With if statements, you can use the logical OR operator to combine multiple tests into a single statement:

This suffers from the same challenges that we presented in the introduction to switch statements: c gets evaluated multiple times and the reader has to make sure it is c that is being evaluated each time.

You can do something similar using switch statements by placing multiple case labels in sequence:

Remember, execution begins at the first statement after a matching case label. Case labels aren’t statements (they’re labels), so they don’t count.

The first statement after all of the case statements in the above program is return true, so if any case labels match, the function will return true.

Thus, we can “stack” case labels to make all of those case labels share the same set of statements afterward. This is not considered fallthrough behavior, so use of comments or [[fallthrough]] is not needed here.

Switch case scoping

With if statements, you can only have a single statement after the if-condition, and that statement is considered to be implicitly inside a block:

However, with switch statements, the statements after labels are all scoped to the the switch block. No implicit blocks are created.

In the above example, the 2 statements between the case 1 and the default label are scoped as part of the switch block, not a block implicit to case 1.

Variable declaration and initialization inside case statements

You can declare (but not initialize) variables inside the switch, both before and after the case labels:

Note that although variable y was defined in case 1, it was used in case 2 as well. 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 a later case, even if the case in which the variable is defined is never executed! Put another way, defining a variable without an initializer is just telling the compiler that the variable is now in scope from that point on. This doesn’t require the definition to actually be executed.

However, initialization of variables is disallowed and will cause a compile error. This is because initializing a variable does require execution, and initialization could be skipped over depending on which cases are executed.

If a case needs to define and/or initialize a new variable, best practice is to do so inside a block underneath the case statement:

Rule

If defining variables used in a case statement, do so in a block inside the case (or before the switch if appropriate)

Quiz time

Question #1


Write a function called calculate() that takes two integers and a char representing one of the following mathematical operations: +, -, *, /, or % (modulus). Use a switch statement to perform the appropriate mathematical operation on the integers, and return the result. If an invalid operator is passed into the function, the function should print an error. For the division operator, do an integer division.

Hint: “operator” is a keyword, variables can’t be named “operator”.

Show Solution


7.6 -- Goto statements
Index
7.4 -- Switch statement basics

36 comments to 7.5 — Switch fallthrough and scoping

  • Hell

    I might be missing something but I notice that initialization in the last case of a switch block is still allowed?? (without a default label)

  • Chayim

    Why does assignment not need execution like initialization ?

    • Alex

      Assignment needs execution too.

      Since a variable definition puts the variable in scope from that point forward, it would be confusing for an initializer to be present but not executed. So initialization is disallowed. Assignment doesn't have the same problem since it's just a value change.

  • Chayim

    In the "The [[fallthrough]] attribute"
    to indicate that fallthough is intentional, missing r to be corrected: "fallthrough

  • Chayim

    What does it mean:
    "C++17 adds a new attribute called [[fallthrough]] that can be used in conjunction with a null statement"
    Is it not a regular statement? so how does it work? what does it mean that it works with conjunction with a null statement? any statement has a semicolon after the statement

    • Alex

      Added some more information about attributes to the lesson. Attributes are not regular statements -- they can be used almost anywhere they are contextually relevant. The [[fallthrough]] attribute modifies a null statement (an empty statement that only contains a terminating semicolon).

  • Chayim

    If the switch function was made to match the expression so why is this function designed to compile and execute all the following cases with fallout? Why not match the expression with the case matching and execute only the matching case?

  • Hugh

    Hi! I don't know what is wrong with this. It keeps saying that I need to include a narrowing conversion, but when I compare my code with other people's I don't see any differences.

    • Alex

      It's not saying you need to include a narrowing conversion, it's saying you are trying to initialize an object (operation) in a way that involves a narrowing conversion, which is disallowed when you use brace-initialization.

      In short, variable "operation" is a char, and you're initializing it with the return value of getOpe(), which is an int. Converting an int to a char is a narrowing conversion.

      getOpe() should return a char.

  • lemonad

    the universe explodes ...

  • Rayyan Khan

    My solution for the Quiz!
    It turned out quite similar to the solution..

  • kibir

    the universe "implodes"... very precise.

  • Tobito

    Any ideas WHY ???

    • Helper

      Hello,
      You initialized x at line 7 meaning only if a==1 and then fallthrough to the following case.
      Otherwise if a==2 only the code in line 10 and the following will be executed (so x will not be initialized).
      Moreover you should avoid using fallthrough except for specific reasons and if you do put the [[fallthrough]] null statement to avoid warnings.

  • nav

  • nuth vireak

    Hi I made it into functions. Is it good?

  • michael oska

    hello
    please you said "illegal: initialization is not allowed within a case" but when i try it in my ide it work fine,what is the reason for that?
    do you mean initialization is allow but not prefer because we might not reach that case?
    thank you for the great tutoriol

  • J34NP3T3R

    What to do if the user input a CHAR instead of something numeric ?
    what is the best way to go about solving this issue ?

    • nascardriver

      A `char` is numeric.

    • Spero

      Pretty sure they are just asking how to handle invalid input without defaulting an error message. It will be taught later and I don't think you should jump ahead but if you feel the need to then it could be handled with recursion(chapter 10.2) and refactoring. Here's my sloppy example for the operator input. I'm sure it could be cleaner or better but I'm not any farther in the lessons than this chapter.

  • Istabi Tystab

    Hi I have a question about the return value of the default case. In your solution for example you return 0, but that means that if the user inputs an invalid operator, lets say calculate(1, 2, 'k') for example, then the  program will print:

    1 k 2 is calculate(): Unhandled case
    0

    But I'd like the program to only print: calculate(): Unhandled case
    How would you do that?
    I though of checking the returned value with a conditional statement and using that to see if I should print it or not, but that wouldn't work because any value I return in the default case could also be an actual value returned from one of the valid cases.
    I could choose a random number with little odds of ever being the result of the user's input, but that can't be the correct way to go about it...

    • nascardriver

      `calculate` could throw an exception or return a type that can indicate errors. We haven't covered either yet.

      `std::optional` isn't covered on learncpp I don't think

  • kio

  • John

    Hi, I'm not sure if it's a good place to leave this comment, I'm doing so because there are few comments and I hope admins can see it.
    I have a question:
    I started to learn cpp for competitive programming purposes and I was wondering if all of that knowledge from this tutorial is required for competitive programming, or I can just study some specific chapters/lessons on this site and then focus on algorithms and data structures? If not all of that knowledge is required, then which parts are? I've already covered lessons up to this chapter. I am looking forward to your reply.

    • nascardriver

      Hi John!

      I've never done competitive programming, so take this with a grain of salt.

      If you're going to write real programs, or games, you'll need everything that is taught on learncpp and more.
      If you're just going to write algorithms to solve very specific problems, you can probably do that after Chapter 10. Knowing more won't hurt. If you're going to stop after Chapter 10, and you're only going to do competitive programming, I don't think you had the need to learn C++ in the first place. You'll only know the features that all C-like languages have in common, meaning you could have done with an easier language like python or js/ts.

      • John

        Okay, thank you! I've chosen c++ because this language and your tutorials were recommended on IOI site and I liked these lessons. You're doing great work guys :).

  • Waldo Lemmer

    Great lesson! Two questions:

    1. If I put more likely cases before less likely cases, could that improve performance?

    2. I wanted to use doubles instead of integers. How does this look?

    • Alex

      The order of switch cases shouldn't matter for performance. Best practice is to list the cases in alpha/numeric order, or whatever order makes for the easiest comprehension.

      For your program:
      * Initialize your variables
      * Operator % doesn't make sense on doubles, you should remove this case.

      Other than that, seems substantially similar to the reference solution.

  • Giang

    I think you should add some tags beside the lesson's names in the Index page so that old visiters like me can find out the lessons you updated. And thanx so so much for this really helpful site!!!

  • Oleg Revedzhuk

    You missed a ]

Leave a Comment

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