Switch Statement

COS1511 - Introduction To Programming I · Learning C++

Switch Statement

Basic Syntax

A switch statement allows you to evaluate a single expression and select one of many code paths based on its value. The basic structure of a switch statement is as follows:

switch (expression) {
case value1:
// code for value1
break;
case value2:
// code for value2
break;
case value3:
// code for value3
break;
default:
// code if no case matches
break; // optional but good practice
}

The components of the switch statement are:

  • switch (expr): Evaluates expr and jumps to the matching case. This is required.
  • case value:: A label for a specific constant value. At least one case is required.
  • break;: Exits the switch block immediately. It is strongly recommended to include this.
  • default:: A catch-all label that runs when no case matches. This is optional but best practice.

Valid Expression Types

The expression in a switch statement must evaluate to an integral or enumeration type. The case values must be compile-time constants. Valid types include:

  • int, short, long, long long (and their unsigned variants)
  • char (treated as its ASCII integer value)
  • enum types

Invalid types include float, double, string, or any non-constant case value.

How It Works

When a switch statement runs, the expression is evaluated once. The program scans the case labels from top to bottom until it finds a match. It then jumps to that label and begins executing statements from that point forward.

Step-by-step Example — Day of the Week

#include <iostream>
using namespace std;

int main() {
    int day = 3;
    switch (day) { // step 1: evaluate 'day' -> 3
        case 1: // step 2: 3 == 1? No, skip
            cout << "Monday";
            break;
        case 2: // step 3: 3 == 2? No, skip
            cout << "Tuesday";
            break;
        case 3: // step 4: 3 == 3? YES -> jump here
            cout << "Wednesday";
            break; // step 5: break -> exit switch
        case 4:
            cout << "Thursday";
            break;
        default:
            cout << "Weekend or invalid";
            break;
    }
    return 0;
}

The output of this code will be:

Wednesday

The steps of execution are:

  1. Evaluate switch expression: day = 3
  2. Compare 3 with case 1: no match, skip
  3. Compare 3 with case 2: no match, skip
  4. Compare 3 with case 3: MATCH — jump to this label
  5. Execute: cout << "Wednesday"
  6. Hit break — exit the entire switch block
  7. Continue program after the closing }

The break Statement

The break statement tells C++ to exit the switch block immediately. Without break, execution continues into the next case. This behaviour is known as fall-through.

Example of Unintentional Fall-through

int choice = 2;
switch (choice) {
    case 1:
        cout << "One" << endl;
        // no break!
    case 2:
        cout << "Two" << endl;
        // no break!
    case 3:
        cout << "Three" << endl;
        // no break!
    default:
        cout << "Default" << endl;
}
// Output: Two
// Three
// Default <-- all three ran!

Watch out: Without break, execution falls through every subsequent case until it hits a break or the closing brace. This is almost always a bug when unintentional.

Example with Break — Correct Behaviour

int choice = 2;
switch (choice) {
    case 1:
        cout << "One" << endl;
        break; // exits after case 1
    case 2:
        cout << "Two" << endl;
        break; // exits after case 2
    case 3:
        cout << "Three" << endl;
        break;
    default:
        cout << "Default" << endl;
        break;
}
// Output: Two

The default Case

The default label acts as a catch-all. It runs when the switch expression does not match any of the listed case values. Although it is optional, it is strongly recommended for robust error handling.

Example — Menu Input Validation

#include <iostream>
using namespace std;

int main() {
    int option;
    cout << "Enter 1, 2, or 3: ";
    cin >> option;
    switch (option) {
        case 1:
            cout << "You chose: New Game" << endl;
            break;
        case 2:
            cout << "You chose: Load Game" << endl;
            break;
        case 3:
            cout << "You chose: Exit" << endl;
            break;
        default:
            cout << "Invalid option. Please enter 1, 2, or 3." << endl;
            break;
    }
    return 0;
}

The default can appear anywhere in the switch, but placing it last is the universal convention for readability.

Fall-through Behaviour

Fall-through can be used intentionally to run the same code for multiple case values. When you deliberately omit break, always add a comment to indicate that it is intentional.

Example — Season Classifier

int month = 4; // April
switch (month) {
    case 12:
    case 1:
    case 2:
        cout << "Winter" << endl;
        break;
    case 3:
    case 4: // month == 4 matches here, falls to case 5
    case 5:
        cout << "Spring" << endl; // <-- this runs
        break;
    case 6:
    case 7:
    case 8:
        cout << "Summer" << endl;
        break;
    case 9:
    case 10:
    case 11:
        cout << "Autumn" << endl;
        break;
    default:
        cout << "Invalid month" << endl;
        break;
}
// Output: Spring

Grouping Cases

Grouping is a clean way to apply one block of code to several values. Stack the case labels immediately above the shared code. No statements between them means controlled fall-through.

Example — Vowel / Consonant Checker

char letter = 'e';
switch (letter) {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
    case 'A':
    case 'E':
    case 'I':
    case 'O':
    case 'U':
        cout << letter << " is a vowel." << endl;
        break;
    default:
        cout << letter << " is a consonant." << endl;
        break;
}
// Output: e is a vowel.

Example — Weekday / Weekend

int day = 6; // Saturday
switch (day) {
    case 1: case 2: case 3: case 4: case 5:
        cout << "Weekday" << endl;
        break;
    case 6: case 7:
        cout << "Weekend" << endl; // <-- runs
        break;
    default:
        cout << "Invalid day" << endl;
        break;
}
// Output: Weekend

switch vs if-else

Both switch and if-else handle multi-way branching, but each has its strengths. Use switch when matching one variable against a list of known constant values. Use if-else for ranges, multiple variables, or complex boolean expressions.

Side-by-side Code Comparison

// switch — clean for exact integer/char matches
switch (grade) {
    case 'A': cout << "Excellent"; break;
    case 'B': cout << "Good"; break;
    case 'C': cout << "Average"; break;
    default: cout << "Below avg"; break;
}

// if-else — required for range-based logic
if (score >= 90) cout << "Excellent";
else if (score >= 75) cout << "Good";
else if (score >= 60) cout << "Average";
else cout << "Below avg";

Common Pitfalls

Watch out: Forgetting break can lead to unintentional fall-through, which is a common bug.

// BUG: missing break in case 1
switch (x) {
    case 1:
        cout << "One" << endl;
        // forgot break!
    case 2:
        cout << "Two" << endl; // also runs when x == 1!
        break;
}
// Fix: add break; at the end of every case

Watch out: Duplicate case values will cause a compile error.

// BUG: two cases with value 2 — compile error!
switch (n) {
    case 2: cout << "First"; break;
    case 2: cout << "Second"; break; // ERROR
}
// Fix: each case value must be unique

Watch out: Using float or string as the switch expression is not allowed.

double price = 9.99;
// BUG: float is not allowed in switch
switch (price) { // compile error!
    case 9.99: ...
}
string name = "Alice";
// BUG: string is not allowed in switch
switch (name) { // compile error!
    case "Alice": ...
}
// Fix: use if-else for floats and strings

Watch out: Declaring variables inside case without braces can lead to scope issues.

// BUG: variable declaration jumps over by another case
switch (x) {
    case 1:
        int y = 10; // compile error in some compilers
        cout << y;
        break;
    case 2:
        cout << "Two";
        break;
}
// Fix: wrap the case body in braces
switch (x) {
    case 1: {
        int y = 10;
        cout << y;
        break;
    }
    case 2:
        cout << "Two";
        break;
}

Summary

  • Always end every case with break; unless fall-through is intentional.
  • Always include a default: case to handle unexpected values.
  • Use switch for exact constant matches — use if-else for ranges and complex conditions.
  • Comment intentional fall-through clearly.
  • Wrap case bodies in { } whenever you declare local variables inside them.
  • Each case value must be unique.

Check your understanding

  • What is the purpose of the break statement in a switch case?
  • When should you use a default case in a switch statement?
  • How can you intentionally create fall-through behaviour in a switch statement?
  • What types of expressions are allowed in a switch statement?