Control Flow: if, switch, loops, and the for idiom
What This Concept Is
C's control flow is minimal and mostly familiar, but the idioms matter because they are what other C programmers read for.
The core pieces:
if (expr) stmt/if (expr) stmt else stmt- any non-zeroexpris "true"switch (expr) { case C1: ... break; default: ... }- integer dispatch with implicit fall-throughwhile (expr) stmt- test then bodydo stmt while (expr);- body then test (runs at least once)for (init; cond; step) stmt- equivalent toinit; while (cond) { stmt; step; }breakexits the innermostswitchor loop;continuejumps to the step of the innermost loopgoto label;jumps within a function; used in well-placed error-handling chains
The for idiom in C is structured loop plumbing: initialization, termination test, and increment all in one place, so the shape of the loop is visible at the top.
Why It Matters Here
The rest of the module and the next module lean on two habits:
- Write loops with a clear termination argument.
- Do not rely on incidental fall-through in
switch; document any intentional one.
Off-by-one and accidental fall-through are two of the most common C bugs. Control-flow discipline is where you stop them.
Concrete Example
The canonical for loop over an array:
int arr[N];
for (size_t i = 0; i < N; i++) {
arr[i] = (int)i;
}
switch with an explicit fall-through:
switch (c) {
case ' ':
case '\t':
case '\n':
handle_whitespace();
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
handle_digit(c);
break;
default:
handle_other(c);
break;
}
Reasonable goto for error handling:
int f(void) {
FILE *fp = fopen("x", "r");
if (!fp) goto fail_fp;
char *buf = malloc(N);
if (!buf) goto fail_buf;
/* ... real work ... */
free(buf);
fclose(fp);
return 0;
fail_buf:
fclose(fp);
fail_fp:
return -1;
}
Common Confusion / Misconception
"switch is a cleaner if/else." It is not. Every case without a terminating break falls into the next one, which is both a feature and a constant source of bugs. Always close each non-fall-through case with break or return.
"goto is always wrong." In C, a forward-only goto to a single cleanup block is idiomatic. What is wrong is backward goto used as a substitute for a loop.
How To Use It
- Default to
for (size_t i = 0; i < n; i++)for counted iteration. Usewhilewhen the termination test is the focus, not the counter. - Every
caseeither ends withbreak,return,goto, or a deliberate comment/* fall through */. - Declare the loop variable inside the
forwhen using C99 or later. - Keep the body of each control-flow construct indented; never omit the braces on multi-line bodies.
- Use
gotoonly for forward jumps to error-cleanup, and only when the alternative is deeper nesting.
Check Yourself
- In
for (i = 0; i < n; i++) sum += a[i];, name three ways you could get an off-by-one. - Why does
switchrequirebreak? - When is
do/whilethe right choice instead ofwhile?
Mini Drill or Application
Write, by hand, without looking them up:
- A
whileloop that reads characters fromstdinuntil EOF usinggetcharand counts newlines. - A
forloop that sumsarr[0..n-1]intolong long sumand avoids signed overflow. - A
switchon acharthat classifies vowels, consonants, digits, whitespace, and other. - An error-handling pattern using a single
goto cleanup;target that releases two resources.