Week 16 · Phase 2 — The Ancestry
while and for — the patient stamina of computers that never tire.
Photo · Laura Ockel / Unsplash
Computers are dazzlingly fast. They are also dazzlingly patient. A modern CPU does three or four billion operations per second and will gladly do them, in a straight repetitive line, until you tell it to stop. The construct that gives you that stamina, in any language you'll ever use, is a loop.
If if is the atom of decision, the loop is the atom of repetition. Together, they are computationally enough — Turing's tape machine had nothing else, and could in principle do everything every modern program does. C gives you three flavours of loop. Almost every other language gives you the same three, with cosmetic variations.
while — repeat as long as a condition is trueint i = 0;
while (i < 5) {
printf("%d\n", i);
i++;
}
Check the condition. If true, run the body. Check again. If still true, run the body again. Continue until the condition is false. while is the most general loop in C — every other loop can be written as a while.
for — when you know how many timesfor (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
Three pieces, separated by semicolons inside the parentheses: initialiser, condition, step. The initialiser runs once before the loop starts; the condition is checked at the start of each iteration; the step runs at the end of each iteration. It's exactly equivalent to the while above — same machine code, different shape — but signals intent: "I'm walking through a fixed range of values".
do { } while — at least one iteration, guaranteeddo {
printf("keep going? (y/n): ");
scanf(" %c", &c);
} while (c == 'y');
Same as while, but the condition is checked after the body runs. Use it when you must do the body once before you can decide whether to continue (reading user input, or trying an operation that might succeed or fail).
Every loop, no matter the syntax, compiles down to roughly the same shape — a couple of labels and a backward jump:
// for (int i = 0; i < 5; i++) { ...body... }
mov w0, #0 // i = 0
.loop_top:
cmp w0, #5 // compare i to 5
b.ge .loop_end // if i >= 5, exit
// ... body ...
add w0, w0, #1 // i++
b .loop_top // jump back
.loop_end:
// continue with the rest of the program
One cmp, one conditional branch, one body, one increment, one unconditional branch back to the top. That's the entire loop machinery, repeated however many times the chef needs to. Five iterations — five trips around. Five million — five million trips. The chef does not get tired. The chef does not get bored.
Almost every loop you'll ever write fits one of these patterns. Recognise them and they fall off your fingers without thinking:
for (int i = 0; i < n; i++) {
total += scores[i];
}
int found_at = -1;
for (int i = 0; i < n; i++) {
if (arr[i] == target) {
found_at = i;
break; // exit the loop now
}
}
int c;
while ((c = getchar()) != EOF) {
putchar(c);
}
This last one is straight out of K&R. It reads characters from stdin and writes them to stdout until end-of-file. Idiomatically, that single line — (c = getchar()) != EOF — assigns the next character into c, then compares the result to EOF, all in one expression. C tolerates this kind of compactness; it has trade-offs in readability that you should consider for yourself.
break and continueTwo keywords that change the flow inside a loop:
break — exit the innermost loop immediately. Useful when you've found what you were looking for, or hit an error.continue — skip the rest of this iteration and go to the next one. Useful for "if this element doesn't match, move on".// print only the even numbers from 0 to 19, but stop at 12
for (int i = 0; i < 20; i++) {
if (i == 12) break; // stop entirely
if (i % 2 != 0) continue; // skip this iteration
printf("%d\n", i);
}
This prints 0 2 4 6 8 10, then breaks out at 12. continue in a for loop still runs the step (i++); it just skips back to the condition check.
Sometimes you actually want a loop that never stops on its own — a daemon that listens forever, an event loop, a game loop. The C idiom is:
for (;;) {
// wait for an event
// handle it
// repeat forever
}
for (;;) is "no initialiser, no condition, no step" — a loop with nothing to stop it. while (1) works equally well. The exit happens via break, or a return, or a signal from the OS, or the end of the universe. Every long-running server in the world has one of these somewhere near its main function.
i <= n when you meant i < n. Or starting at 1 instead of 0. Always count carefully: an array of size n has indices 0 to n-1 inclusive.done ever becomes true. The chef will spin happily forever, drawing 100% CPU. Ctrl+C is your friend.size_t against a negative number. Common in C: for (size_t i = n - 1; i >= 0; i--) never terminates because size_t is unsigned and goes from 0 to a huge positive number, not to -1. Use a signed type or restructure as for (size_t i = n; i-- > 0;).Almost every line of an AI training run is, ultimately, a loop. The outermost loop walks epochs (passes through your dataset). Inside that, batches. Inside that, layers. Inside that, individual matrix elements — billions of them per batch.
The interesting part is that, by the time you get down to the innermost loop, the whole thing has been reorganised: the explicit Python for i in range(...) has been replaced with a single matrix operation, which is itself a heavily-optimised C++/CUDA kernel that uses SIMD and tiling and prefetching to keep the chef perfectly fed. The loop is still there. It's just been compressed into a single very hot, very specialised kernel. Vectorising a loop — turning for i in range into vector_op(...) — is one of the most common, highest-leverage optimisations in numerical code.
And the train_loop at the very top is, often, just while not converged: ... — a literal infinite loop interrupted by a stopping criterion. The whole AI revolution rests on a few well-tuned fors.
The loop is the engine of every long-running computation in history. Three keywords in C; the entire fabric of modern computing.
for loops.for loop. Time it. Run it again with the loop counter as long long and again with int — see if you can detect a difference../copy < some_file.txt.You can now write functions that use variables, decide things, and repeat. You're a real programmer in a small but real way. Next week we descend one floor — into the part of C that other languages chose to abstract away because it was, quite genuinely, terrifying.
Week 17 is The Scary Part — pointers and memory addresses. Where C reveals what's actually been happening this whole time, and where every other language's "kindness" is, in truth, an enormous lie.
Photo free under the Unsplash license. Gears · Laura Ockel.