C / BRANCHING AND SWITCH
break inside switch and the missing-break bug
Predict and fix switch bugs caused by a missing break, and explain why break inside a switch nested in a loop ends only the switch.
What you will learn
- Trace a switch as: jump to the matching label, then run on until a break
- Predict the value a variable holds when one case is missing its break
- Know that break inside a switch inside a loop ends only the switch
- Prevent the bug with a return per case or -Wimplicit-fallthrough warnings
Understanding break inside switch and the missing-break bug
A switch is not a set of separate branches the way a chain of if/else is. The controlling expression is evaluated once, control jumps to the matching case label, and from there execution continues forward through the switch body, crossing later case labels as if they were not there. Labels are addresses, not walls. break is what turns that straight run into a single branch: it transfers control to the first statement after the switch's closing brace.
Because break attaches to the nearest enclosing breakable statement, its meaning changes with nesting. Inside a switch that sits in a for or while, break ends the switch and leaves you at the bottom of the loop body, so the loop takes another turn; C has no leveled break, so ending the loop needs a flag, a goto past it, or a return. The break in the last case or in default is not required, since control reaches the closing brace anyway, but writing it means that appending a new label underneath later cannot silently change the branch above.
A forgotten break is legal C, so there is no compile error; the program simply does more than you wrote. Two symptoms are worth memorising: extra side effects, such as a second line printed or a counter bumped twice, and last-write-wins on assignments, where a variable ends up holding the value from the case below the one that matched. GCC and Clang can flag it with -Wimplicit-fallthrough, which GCC also enables under -Wextra, and it stays quiet where a fallthrough is explicitly marked, so the warning separates intent from accident. When a switch only computes a value, returning from each label removes the possibility altogether.
<stdio.h>
/* breaks are missing on case 1 and case 2 */
static void buggy(int level)
{
printf("buggy(%d): ", level);
switch (level) {
case 1:
printf("[low]");
case 2:
printf("[medium]");
case 3:
printf("[high]");
break;
default:
printf("[unknown]");
}
putchar('\n');
}
static void fixed(int level)
{
printf("fixed(%d): ", level);
switch (level) {
case 1:
printf("[low]");
break;
case 2:
printf("[medium]");
break;
case 3:
printf("[high]");
break;
default:
printf("[unknown]");
break;
}
putchar('\n');
}
int main(void)
{
for (int level = 1; level <= 3; level++) {
buggy(level);
fixed(level);
}
return 0;
}
In a switch, case labels are only jump targets, and break is the thing that stops execution from running on into the next case's statements.
Worked examples
break does not leave the loop
A case that is supposed to stop iteration needs a flag, because its break only ends the switch.
<stdio.h>
int main(void)
{
int values[] = {4, 7, -1, 9};
int stop = 0;
int i;
for (i = 0; i < 4 && !stop; i++) {
switch (values[i]) {
case -1:
printf("sentinel at index %d\n", i);
stop = 1;
break; /* leaves the switch only */
default:
printf("value %d\n", values[i]);
break;
}
}
printf("loop ended at i=%d\n", i);
return 0;
}
Example explained
Line 1case -1 sets stop = 1, and its break jumps past the switch's closing brace, which is still inside the loop body.
Line 2Control then reaches i++ and the loop condition, where !stop is false, so iteration ends there.
Line 3Without the stop flag the loop would also print value 9; break alone cannot leave a loop from inside a switch.
Line 4i prints as 3 because the increment ran before the condition was re-tested.
Missing break silently changes a value
A forgotten break can produce a wrong number with no duplicated output to hint at it.
<stdio.h>
static int fee_broken(char tier)
{
int fee = 0;
switch (tier) {
case 'a':
fee = 10;
case 'b':
fee = 25;
break;
case 'c':
fee = 50;
break;
}
return fee;
}
static int fee_ok(char tier)
{
switch (tier) {
case 'a': return 10;
case 'b': return 25;
case 'c': return 50;
default: return 0;
}
}
int main(void)
{
printf("broken a=%d b=%d c=%d\n",
fee_broken('a'), fee_broken('b'), fee_broken('c'));
printf("ok a=%d b=%d c=%d\n",
fee_ok('a'), fee_ok('b'), fee_ok('c'));
return 0;
}
Example explained
Line 1case 'a' assigns 10 and has no break, so control continues into case 'b' and overwrites fee with 25.
Line 2Nothing is printed twice, which is why this shape of bug survives eyeballing: only the returned number is wrong.
Line 3fee_ok returns from each label, so control leaves the function at once and a forgotten break cannot exist.
Line 4fee_broken has no default, so an unmatched tier falls out of the switch and returns the initial 0.
Important notes
continue inside a switch has nothing to do with the switch: it belongs to the enclosing loop and abandons the rest of the switch body along with the rest of that iteration.
A missing break is never a compile error, since falling through is valid C, so the only automatic help is a warning you have to enable and then actually read.
Common mistakes
Wrapping a case body in braces and assuming that ends the branch; braces only scope declarations, so control still falls into the next label's statements.
Using break inside a switch to leave the surrounding while loop, which keeps spinning, and hangs the program if that case was the only exit.
Leaving the final case without a break and later adding a new case under it, so the previously correct branch starts running the new code too.
Try it yourself
Change, predict, then run
In a browser editor, write a switch on int n whose cases 1, 2 and 3 assign total = 10, 20 and 40 with no break anywhere, print total for n = 1, 2 and 3, and write down your three predicted numbers before running it. Then add the breaks and compare the two runs.
Open the C workspaceCheck your understanding
A while loop contains a switch, and the case for 'q' prints a message and then breaks, intending to end the loop. The loop condition is never changed. Why does the program keep looping?
- break ended the switch, and control resumed at the bottom of the loop body, so the condition was tested again
- break exits the loop only when it is the last statement in the switch body
- C requires break 2; to leave both the switch and the loop at once
- The switch has no default label, so 'q' was never actually matched
Show answer
break binds to the nearest enclosing switch or loop, and here the switch is nearer, so control jumps just past the switch's closing brace, a point that is still inside the loop body. Option 2 is a rule borrowed from other languages: C has no leveled break, which is precisely why the flag, goto or return patterns exist for this case.