C Switch

The switch statement lets a C program choose one path from several fixed possibilities. It is useful when one expression, such as a menu number, command character, enum value, or status code, must be compared with exact constant values. A good switch can be easier to read than a long chain of else if tests because the choices are laid out as named labels.

Overview: How switch Works

A switch starts by evaluating its controlling expression exactly once. That value is compared with each case label inside the switch body. If a matching label is found, execution jumps to the statement after that label. If no label matches and a default label exists, execution jumps to default. If no label matches and there is no default, the whole switch is skipped.

In C, the controlling expression must have an integer type after the usual integer promotions. That includes types such as int, char, short, long, unsigned integer types, and enumeration types. You cannot switch directly on strings, floating-point values, arrays, or structures. Each case value must be an integer constant expression, meaning the compiler must know it at compile time. A literal such as 3, a character constant such as 'q', an enum constant, or a macro that expands to a constant can be a case label. A normal variable cannot be a case label.

The most important behavior to understand is that labels do not form automatic blocks. After C jumps to a matching case, execution continues statement by statement until it reaches a jump statement such as break, return, or goto, or until the switch body ends. Continuing into the next case is called fall-through. Fall-through is legal and sometimes useful, but accidental fall-through is one of the most common switch bugs.

Compilers often implement dense switches with a jump table: an array of target addresses indexed by the controlling value. Sparse switches may be compiled as a chain of comparisons or a search structure. You do not need to choose the implementation; write clear code and let the compiler optimize. The source-level rule is always the same: evaluate the expression once, find the matching label, then continue executing from there.

Syntax

This complete program shows the general shape of a switch. The cases are constants, each case has statements, and break leaves the switch after the selected branch runs.

#include <stdio.h>

int main(void)
{
    int choice = 2;

    switch (choice) {
        case 1:
            printf("First option\n");
            break;
        case 2:
            printf("Second option\n");
            break;
        default:
            printf("Unknown option\n");
            break;
    }

    return 0;
}

Output:

Second option
Part Meaning
switch (choice) Evaluates choice once and uses its value to select a label.
case 1: A label that can be selected when the switch value equals 1.
Statements after a case The code that runs after C jumps to the matching label.
break; Exits the switch so execution continues after the closing brace.
default: Optional fallback label used when no case matches.

Examples

Choosing a Weekday

A basic switch is ideal when an integer code maps to a small set of names.

#include <stdio.h>

int main(void)
{
    int day = 4;

    switch (day) {
        case 1:
            printf("Monday\n");
            break;
        case 2:
            printf("Tuesday\n");
            break;
        case 3:
            printf("Wednesday\n");
            break;
        case 4:
            printf("Thursday\n");
            break;
        case 5:
            printf("Friday\n");
            break;
        default:
            printf("Weekend or invalid day\n");
            break;
    }

    return 0;
}

Output:

Thursday

The value of day is 4, so execution starts at case 4. The program prints Thursday, then break exits the switch. The default branch does not run because a matching case was found.

Grouping Several Cases

Several labels can share the same statements. This is a controlled use of fall-through: empty labels intentionally fall through to one shared block.

#include <stdio.h>

int main(void)
{
    char grade = 'B';

    switch (grade) {
        case 'A':
        case 'B':
            printf("Strong result\n");
            break;
        case 'C':
            printf("Passing result\n");
            break;
        case 'D':
        case 'F':
            printf("Needs improvement\n");
            break;
        default:
            printf("Unknown grade\n");
            break;
    }

    return 0;
}

Output:

Strong result

The labels 'A' and 'B' both lead to the same message. Because grade is a character, C compares its integer character code with the case labels. Character constants are integer constants in C, so they are valid case values.

Using an Enum for Status Codes

Enums make switches easier to read because each case has a meaningful name instead of a bare number.

#include <stdio.h>

enum OrderStatus {
    ORDER_NEW = 1,
    ORDER_PAID = 2,
    ORDER_SHIPPED = 3,
    ORDER_CANCELLED = 4
};

int main(void)
{
    enum OrderStatus status = ORDER_SHIPPED;

    switch (status) {
        case ORDER_NEW:
            printf("Create invoice\n");
            break;
        case ORDER_PAID:
            printf("Prepare package\n");
            break;
        case ORDER_SHIPPED:
            printf("Send tracking email\n");
            break;
        case ORDER_CANCELLED:
            printf("Stop fulfillment\n");
            break;
        default:
            printf("Unknown order status\n");
            break;
    }

    return 0;
}

Output:

Send tracking email

The enum constants are compile-time integer constants, so they work naturally as case labels. This style is common when a program has a known list of modes, states, command types, or error codes.

How It Works Step by Step

  1. C evaluates the expression inside switch (...). Side effects in that expression happen once, not once per case.
  2. The resulting integer value is compared against the case labels after integer promotions. For example, a char switch value is promoted to int for the comparison.
  3. If a matching case exists, execution jumps to that label. If not, execution jumps to default if it exists.
  4. Statements run in normal order from the jump point. Case labels are labels, not boundaries.
  5. A break exits the nearest enclosing switch or loop. Inside a switch, it usually means “this case is finished.”
  6. If there is no break, execution falls through into later labels until another jump statement or the end of the switch is reached.

The default label can appear anywhere in the switch body, although most C code places it last because that is easiest to scan. Its position does not change when it is selected: it is still just the fallback jump target.

Common Mistakes

Forgetting break

If a case prints a message but does not end with break, C continues into the next case. That may print extra messages or run code meant for a different choice. Add break unless fall-through is clearly intentional. When fall-through is intentional, keep the shared code immediately after grouped labels so the intent is obvious.

Using Non-Constant Case Values

A case label cannot use a normal variable because the compiler needs the labels while compiling the switch. For example, a variable named target is not valid as case target:. Use a literal, #define constant, enum constant, or other integer constant expression.

Trying to Switch on Strings or Ranges

C strings are arrays of characters, not single integer values, so switch (name) cannot choose among string contents. Use strcmp with if and else if for strings. Likewise, a case label matches one exact value, not a range such as “80 through 89.” Use if when the question is based on comparisons like score >= 80.

Declaring Variables Directly After a Case Label

A case label is a label before a statement. If a branch needs local variables, use braces to create a small block after the case label. This also limits the variable scope to that branch and avoids confusing jumps over initializations.

#include <stdio.h>

int main(void)
{
    int command = 1;

    switch (command) {
        case 1: {
            int retries = 3;
            printf("Retry limit: %d\n", retries);
            break;
        }
        case 2:
            printf("Cancel command\n");
            break;
        default:
            printf("Unknown command\n");
            break;
    }

    return 0;
}

Output:

Retry limit: 3

The braces after case 1: create a normal compound statement. That is the cleanest way to declare and initialize variables that belong only to one case.

Best Practices

  • Use switch for exact choices over integer-like values: commands, menu items, enum states, character codes, and small numeric identifiers.
  • Use if and else if for ranges, string comparisons, floating-point comparisons, and conditions that combine several boolean expressions.
  • Include a default branch unless every possible value is deliberately handled elsewhere. A default branch helps catch invalid input and future changes.
  • Put break, return, or another clear exit at the end of each non-empty case.
  • Group labels only when they truly share the same behavior. Do not rely on accidental fall-through to save a line or two.
  • Prefer enums over unexplained numbers when the cases represent named states or categories.
  • Keep each case short. If a case grows large, move the work into a function and call that function from the switch.

Practice Exercises

  1. Write a program that stores a menu choice from 1 to 4 and prints New file, Open file, Save file, or Exit. Add a default branch for invalid choices.
  2. Create an enum named TrafficLight with values for red, yellow, and green. Use a switch to print what a driver should do for each light.
  3. Write a calculator for a fixed expression such as 12 and 4 with an operator character '+', '-', '*', or '/'. Use switch to choose the operation and handle unknown operators.

Summary

  • switch chooses among exact constant values of an integer-like expression.
  • The controlling expression is evaluated once, then C jumps to the matching case or to default.
  • case labels must be integer constant expressions; variables, strings, and ranges are not valid case labels.
  • break prevents execution from falling into the next case.
  • Intentional fall-through is useful for grouped labels, but accidental fall-through is a common bug.
  • Enums and meaningful constants make switch statements clearer than unexplained numbers.