The switch statement looks simple until it silently does something you did not expect. Fall-through is the most misunderstood behaviour in C control flow. Here is everything you need to know to use switch correctly.
Basic Syntax
#include <stdio.h>
int main() {
int day = 3;
switch (day) {
case 1:
printf("Mondayn");
break;
case 2:
printf("Tuesdayn");
break;
case 3:
printf("Wednesdayn");
break;
default:
printf("Other dayn");
break;
}
return 0;
}
The expression after switch is evaluated once. Control jumps to the matching case label. If no case matches, control goes to default. If there is no default, execution continues after the closing brace.
Fall-Through — The Most Common Source of Bugs
Without a break, execution falls through to the next case. This is intentional by design — but it surprises every programmer at least once.
#include <stdio.h>
int main() {
int x = 1;
switch (x) {
case 1:
printf("Onen");
/* no break — falls through */
case 2:
printf("Twon");
/* no break — falls through */
case 3:
printf("Threen");
break;
}
return 0;
}
Output:
One
Two
Three
All three statements print even though x == 1. Once control enters at case 1, it executes every statement until it hits a break or the end of the switch.
Intentional fall-through
Fall-through is useful when multiple cases share the same code:
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
days = 31;
break;
case 4:
case 6:
case 9:
case 11:
days = 30;
break;
case 2:
days = 28;
break;
}
Empty case labels (no code, no break) fall through silently. When fall-through is intentional and there is code above the next case, add a comment so the next programmer does not “fix” it:
case 1:
printf("First actionn");
/* intentional fall-through */
case 2:
printf("Second actionn");
break;
Why You Cannot Use Strings in switch
char *lang = "C";
switch (lang) { /* ERROR: not an integer expression */
case "C":
printf("Cn");
break;
}
The switch expression must be an integer type: int, char, enum, or similar. Strings are pointers, not integers. Use if-else chains with strcmp for string comparisons:
#include <string.h>
if (strcmp(lang, "C") == 0) {
printf("Cn");
} else if (strcmp(lang, "C++") == 0) {
printf("C++n");
}
switch with Characters and Enums
#include <stdio.h>
int main() {
char grade = 'B';
switch (grade) {
case 'A':
printf("Excellentn");
break;
case 'B':
printf("Goodn");
break;
case 'C':
printf("Averagen");
break;
default:
printf("Invalid graden");
}
return 0;
}
typedef enum { RED, GREEN, BLUE } Color;
Color c = GREEN;
switch (c) {
case RED: printf("Redn"); break;
case GREEN: printf("Greenn"); break;
case BLUE: printf("Bluen"); break;
}
Using switch with enums is especially clean because the compiler can warn you when a case is missing. Compile with -Wswitch (included in -Wall) and GCC will flag unhandled enum values.
Variables Inside switch Cases
switch (x) {
case 1:
int y = 10; /* may cause a compiler warning or error */
printf("%dn", y);
break;
case 2:
printf("Twon");
break;
}
Declaring variables directly inside a case (without braces) is problematic — the variable is technically in scope for all cases after it, but it is only initialised in one of them. Wrap the case body in braces:
switch (x) {
case 1: {
int y = 10; /* scoped to this block only */
printf("%dn", y);
break;
}
case 2:
printf("Twon");
break;
}
default — Put It Last, or Not
The default case can appear anywhere, but putting it last is the convention and the clearest. If you put it in the middle, execution still falls through to the next case unless you add a break. Always add a break to default.
switch (x) {
default:
printf("Unknownn");
/* no break — falls through to case 1! */
case 1:
printf("Onen");
break;
}
If x is not 1 here, you get both “Unknown” and “One”. This is almost never what you want.
Common Mistakes Summary
- Missing break — falls through to the next case silently
- Using a string — switch only works with integer types
- Declaring variables without braces — scope bleeds across cases
- default in the middle without break — falls through to the next case
- Forgetting default entirely — no-op if no case matches, which can hide logic errors
You can test all of these behaviours directly in our c compiler online — paste any of the examples above and see the output immediately.
If a missing break in your switch is causing an unexpected crash, check our guide on segmentation fault causes — a jumped-to case can corrupt a pointer if the variable declaration was skipped.
TL;DR
- Every case needs a
breakunless fall-through is intentional — comment it when it is - switch only works with integer types — no strings, no floats
- Wrap case bodies in braces when declaring variables
- Always include a
defaultcase and always break out of it - Compile with
-Wswitchto catch unhandled enum values