Intuition What this page is
The parent note taught you the rules of if, switch, loops, and jumps. Rules are easy to read and easy to mis-apply. Here we trace real code by hand , one case class at a time, until there is no situation left that can surprise you. We start every example by asking you to predict the output — because the only way to know you understand control flow is to run it in your head before the compiler does.
Control flow has a finite number of "shapes" it can throw at you. If you can trace one example of every row below, you have covered the whole topic.
#
Case class
The tricky thing
Covered by
A
if/else chain, all sign branches
first-true-wins; ==0 boundary
Ex 1
B
Assignment-in-condition bug
= vs ==
Ex 2
C
switch with intentional fall-through
missing break groups cases
Ex 3
D
switch with an accidental missing break
fall-through is a bug here
Ex 4
E
Loop that runs zero times
while tests first
Ex 5
F
Loop that runs exactly once
do-while tests after
Ex 5
G
for ↔ while translation with continue
the infinite-loop trap
Ex 6
H
break vs continue in one loop
who skips, who leaves
Ex 7
I
Nested loops + goto escape
break only exits one level
Ex 8
J
Real-world word problem
input-validation menu
Ex 9
K
Exam-style twist
fall-through + loop counting combined
Ex 10
The columns below trace each of these. The figures show the path execution actually walks , arrow by arrow.
Worked example Ex 1 — classify three numbers (cell A)
int vals [] = { 7 , 0 , - 4 };
for ( int k = 0 ; k < 3 ; k ++ ) {
int n = vals [k];
if (n > 0 ) printf ( "pos " );
else if (n == 0 ) printf ( "zero " );
else printf ( "neg " );
}
Forecast: write down the three words before reading on.
n = 7. Check n > 0 → 7 > 0 is true. Why this step? In an if/else if/else chain, C tests conditions top to bottom and stops at the first true one . True here, so print pos and skip the rest.
n = 0. Check n > 0 → 0 > 0 is false. Move to else if: n == 0 → true. Why this step? The 0 boundary is exactly why we need a separate ==0 branch — > alone can never catch it. Print zero .
n = -4. -4 > 0 false, -4 == 0 false → land on the final else. Why this step? else is the "none of the above" catch-all; every negative number funnels here. Print neg .
Output: pos zero neg
Verify: three inputs, three outputs, one word each — the chain is mutually exclusive (exactly one branch runs per number), so counts must match. ✓
Worked example Ex 2 — what does this print? (cell B)
int x = 3 ;
if (x = 0 ) printf ( "A" );
else printf ( "B" );
printf ( " x= %d " , x);
Forecast: guess the two things printed.
The condition is x = 0, not x == 0. A single = is assignment : it stores 0 into x. Why this step? In C, an assignment is also an expression whose value is the value assigned — here 0.
The if tests that value. The condition's value is 0, and 0 means false. Why this step? C has no separate boolean requirement: false is exactly 0, true is anything else. So we take the else. Print B.
x was changed as a side effect. x now holds 0. Print x=0.
Output: B x=0
Verify: the danger is that if (x = 5) (non-zero) would always take the if. Here we happened to assign 0, so it always takes the else — either way the branch is decided by the assigned value , never by comparison. The safe habit if (0 == x) would turn the typo into a compile error. ✓
Worked example Ex 3 — grouped cases (cell C)
char c = 'b' ;
switch (c) {
case 'a' :
case 'b' :
case 'c' : printf ( "vowel-ish? no, first three letters" ); break ;
case 'e' : printf ( "e" ); break ;
default : printf ( "other" );
}
Forecast: which message prints?
Compute the switch expression: c is 'b'. Why this step? switch compares an integer/char value against constant labels; 'b' is really the integer 98.
Jump to case 'b':. There is no code and no break under 'a' or 'b'. Why this step? A switch is a computed jump into a block , not separate boxes. Landing on 'b' means execution keeps flowing downward.
Fall through into case 'c':'s body. It prints the message, then hits break, which exits the switch . Why this step? The empty 'a'/'b' labels deliberately share 'c''s code — that is how you group cases.
Output: first three letters (the full string).
Verify: 'a', 'b', 'c' all reach the same printf and same break; 'e' and anything else do not. Grouping = 3 labels, 1 action. ✓
Worked example Ex 4 — the missing
break (cell D)
int menu = 1 ;
switch (menu) {
case 1 : printf ( "open " );
case 2 : printf ( "save " ); // programmer FORGOT break above
case 3 : printf ( "close " ); break ;
default : printf ( "bad" );
}
Forecast: the programmer meant only open to print. What actually prints?
menu == 1 → jump to case 1. Print open . Why this step? correct so far.
No break after case 1. Execution keeps falling into case 2. Print save . Why this step? the switch does not "re-check" menu at each label — labels are just entry points , and once inside we run straight down until a break.
Still no break, fall into case 3. Print close , then finally break stops us. Why this step? only case 3 has the break, so it's the first exit we meet.
Output: open save close — three words when one was intended.
Verify: compare with Ex 3. There the missing breaks were deliberate (empty grouped labels). Here they sit above non-empty bodies → silent bug. Rule: put break on every case unless you write a /* fall through */ comment. ✓
while vs do-while on a false condition (cells E, F)
int i = 5 ;
printf ( "while: " );
while (i < 3 ) { printf ( " %d " , i); i ++ ; } // condition false at start
int j = 5 ;
printf ( "| do: " );
do { printf ( " %d " , j); j ++ ; } while (j < 3 );
Forecast: how many numbers does each loop print?
while checks first. i < 3 is 5 < 3 = false before the body ever runs. Why this step? while is "test-first" — if false at entry, the body executes zero times (cell E).
So the while prints nothing. Output so far: while: .
do-while runs the body first, then tests. It prints 5, does j++ (now j=6), then checks j < 3 = 6 < 3 = false → stop. Why this step? do-while is "test-after" — the body always runs at least once , even when the condition is false (cell F).
Output: while: | do: 5
Verify: the only difference between these two loops is where the test sits. Same variable, same start value, same condition → while = 0 iterations, do-while = 1 iteration. That single-iteration guarantee is exactly why do-while fits menus and input validation. ✓
Worked example Ex 6 — translate a
for and see it break (cell G)
The for loop below prints odd numbers under 6:
for ( int i = 0 ; i < 6 ; i ++ ) {
if (i % 2 == 0 ) continue ; // skip evens
printf ( " %d " , i);
} // prints 1 3 5
A student rewrites it as a while. Trace their version:
int i = 0 ;
while (i < 6 ) {
if (i % 2 == 0 ) continue ; // BUG
printf ( " %d " , i);
i ++ ;
}
Forecast: does the while version also print 1 3 5?
In the for, continue jumps to the update i++ first, then the condition. So skipping an even still advances i. Why this step? the recipe for(A;B;C) guarantees C runs after every body pass, continue included.
In the while, continue jumps straight to the condition — skipping the manual i++. Why this step? the update is now inside the body, after the continue, so a skipped iteration never reaches it.
Start i = 0: 0 % 2 == 0 true → continue → back to condition with i still 0. Forever. Why this step? i never changes when it's even, so the condition never turns false → infinite loop .
Output: the for prints 1 3 5 ; the while hangs .
Verify: the fix is to do the update before any continue (or keep the counter increment out of the skippable region). The correct for≡while mapping is A; while(B){ S; C; } — but continue inside S must still let C run, which the naïve rewrite violated. ✓
Worked example Ex 7 — who skips, who leaves (cell H)
for ( int i = 0 ; i < 6 ; i ++ ) {
if (i == 2 ) continue ; // skip this pass
if (i == 5 ) break ; // leave the loop
printf ( " %d " , i);
}
Forecast: list the printed numbers.
i = 0: neither condition → print 0. i = 1: print 1.
i = 2: continue fires → the printf below is skipped, jump to i++. Why this step? continue abandons only the rest of this iteration , the loop lives on. So 2 is never printed.
i = 3: print 3. i = 4: print 4.
i = 5: break fires → leave the whole loop before reaching printf. Why this step? break exits the innermost loop entirely ; 5 is never printed and iterations >5 never happen.
Output: 0 1 3 4
Verify: continue removed one interior value (2); break truncated the tail (5 and beyond). Remaining set = {0,1,3,4}. ✓
Worked example Ex 8 — escape both loops (cell I)
Find the first cell equal to 9 in a grid and report its position.
int grid [ 3 ][ 3 ] = {{ 1 , 2 , 3 },{ 4 , 9 , 6 },{ 7 , 8 , 0 }};
int fi = - 1 , fj = - 1 ;
for ( int i = 0 ; i < 3 ; i ++ )
for ( int j = 0 ; j < 3 ; j ++ )
if ( grid [i][j] == 9 ) { fi = i; fj = j; goto found; }
found:
printf ( "found at ( %d , %d )" , fi, fj);
Forecast: which (i,j) prints?
Scan row-major: i=0 gives 1 2 3 — no match. Why this step? the outer loop walks rows, the inner walks columns; we visit (0,0),(0,1),(0,2) first.
i=1, j=1 holds 9. Record fi=1, fj=1, then goto found. Why this step? a plain break here would exit only the inner j loop; the outer i loop would continue — goto leaps out of both levels at once .
Land on found: and print. Why this step? the label sits after both loops, so control resumes there with the loops abandoned.
Output: found at (1,1)
Verify: scanning row-major, 9 first appears at row 1, column 1 (0-indexed). No earlier cell equals 9. ✓
Worked example Ex 9 — retry-until-valid PIN (cell J)
"Keep asking for a PIN. Accept only when it equals 4242. Print how many tries it took." Simulate with a queued sequence of inputs {1111, 9999, 4242}.
int inputs [] = { 1111 , 9999 , 4242 };
int idx = 0 , tries = 0 , pin;
do {
pin = inputs [idx ++ ]; // "ask" for a PIN
tries ++ ;
} while (pin != 4242 );
printf ( "unlocked in %d tries" , tries);
Forecast: how many tries?
do-while fits because we must ask at least once . Why this step? you cannot validate a PIN you haven't collected yet — the body must run before the first test (cell F logic, applied for real).
Try 1: pin = 1111, tries = 1, test 1111 != 4242 true → loop again. Try 2: pin = 9999, tries = 2, still != 4242 → loop.
Try 3: pin = 4242, tries = 3, test 4242 != 4242 false → exit. Why this step? the condition finally becomes false, ending the loop on a good input.
Output: unlocked in 3 tries
Verify: 3 inputs consumed, the 3rd matches → tries == 3. A while here would also work but reads worse ("check before asking"); do-while matches the human phrasing "ask, then decide." ✓
Worked example Ex 10 — fall-through inside a loop (cell K)
int total = 0 ;
for ( int i = 0 ; i < 4 ; i ++ ) {
switch (i) {
case 0 : total += 1 ; // no break
case 1 : total += 10 ; break ;
case 2 : total += 100 ; break ;
default : total += 1000 ;
}
}
printf ( " %d " , total);
Forecast: compute total before reading.
i=0: land on case 0 → +1 (total 1). No break → fall into case 1 → +10 (total 11), then break. Why this step? the missing break under case 0 chains it into case 1's add — this iteration contributes 11, not 1.
i=1: land on case 1 → +10 (total 21), break. Contributes 10.
i=2: case 2 → +100 (total 121), break. Contributes 100.
i=3: no matching case → default → +1000 (total 1121). Why this step? default catches any value with no label, here 3.
Output: 1121
Verify: per-iteration contributions 11 + 10 + 100 + 1000 = 1121. The trap is step 1's fall-through adding an extra 10 you'd miss if you treated case like an isolated if. ✓
Recall Which loop runs its body at least once no matter what?
do-while — it tests after the body. A while with a false condition runs zero times.
Recall Why does the hand-translated
while in Ex 6 hang?
continue jumps to the condition, skipping the manual i++ that sits after it, so the counter never advances → infinite loop. In a for, the update C still runs after continue.
Recall When must you reach for
goto over break?
When escaping nested loops: break leaves only the innermost loop, goto (or a flag variable) can leave all levels at once.
Mnemonic The three trap words
Assign, Fall, Skip — = accidentally assigns (Ex 2), a missing break falls through (Ex 4/10), continue skips the update in a hand-made while (Ex 6). Every classic control-flow bug is one of these three.
See also: C Operators · Loops and Time Complexity · Boolean Logic · Functions in C · Recursion
In Ex 5, how many numbers does the while (i<3) with i=5 print? Zero — the condition is false before the body runs.
In Ex 6, why does the while version hang? continue jumps to the condition and skips the i++ after it, so i never advances.
In Ex 8, why not use break to exit both loops? break exits only the innermost loop; goto escapes both at once.
In Ex 10, what is the final total? 1121 — the missing break under case 0 adds an extra 10.