Loops: for, while, do-while

Loops are how a handful of lines of code can process thousands of values without being copy-pasted thousands of times — the three loop forms in C cover every repetition pattern you'll ever need.

Almost everything a computer is actually good at comes down to doing something over and over — printing a list of items, checking every entry in an array, retrying a prompt until the input is valid. A loop is what lets a handful of lines of code carry out that repetition instead of being copy-pasted by hand for every single pass. C gives you three ways to write one — for, while, and do-while — and this page works through all three from first principles: what each one looks like, exactly how control moves through it step by step, how break and continue redirect a loop mid-flight, and finally how to pick the right shape for a given job.

Why loops are needed

Suppose the goal is simply to print the numbers 1 through 5. Without any repetition construct, that means writing the same kind of statement five separate times, changing only the number.

This works, but it doesn't scale — printing 1 through 5,000 this way would mean 5,000 lines of near-identical code, and changing the range at all means editing every one of them by hand.

A loop replaces all five of those statements with one small block that C runs repeatedly on its own — and stretching the range to 5,000 means changing a single number, not adding thousands of lines.

This is the whole reason loops exist: they let a fixed amount of code handle a variable, even unpredictable, amount of work. In practice that covers things like walking through every element of an array, summing or searching a list of values, printing a table or pattern, reading input repeatedly until it's valid, and running the core update-and-redraw cycle of a game or simulation once per frame.

printf("1\n");
printf("2\n");
printf("3\n");
printf("4\n");
printf("5\n");
for (int i = 1; i <= 5; i++) {
    printf("%d\n", i);
}

The three loop types in C at a glance

C has exactly three looping constructs — for, while, and do-while. All three repeat a block of code based on a condition; where they differ is when that condition gets checked and, as a direct consequence, whether the loop's body is guaranteed to run at all.

for and while are check-first loops: if the condition is false on the very first check, the body never executes at all. do-while is a check-last loop: the body always gets its first run before the condition is even looked at.

Beyond that timing difference, the three forms are interchangeable in what they can compute — anything written with one can be rewritten with either of the others. Which one to reach for is mostly about which shape most naturally matches the problem, a theme this page returns to at the end.

for      -> condition checked BEFORE each pass  -> body can run 0 or more times
while    -> condition checked BEFORE each pass  -> body can run 0 or more times
do-while -> condition checked AFTER each pass   -> body always runs at least 1 time

The for loop

A for loop packs everything needed to count into a single header, split into three parts by semicolons: an initialization, a condition, and an update.

Prints the numbers 1 through 5, one per line — the loop counter i is what changes on each pass.

for (initialization; condition; update) {
    // body
}
#include <stdio.h>

int main() {
    for (int i = 1; i <= 5; i++) {
        printf("%d\n", i);
    }
    return 0;
}

How a for loop actually executes, step by step

The three parts of a for header don't all run at the same rate. The initialization runs exactly once, right before anything else. Then C repeatedly checks the condition, runs the body if it's true, and runs the update — in that order — until the condition finally comes back false.

Notice that update always happens after the body and before the condition is checked again — it never runs before the very first condition check.

initialization  (runs once)
      |
      v
  condition ------- false ------> loop ends
      |
     true
      |
      v
    body
      |
      v
   update
      |
      `------------- back to condition

Tracing the exact order of execution

Following one small example through every single step makes the order concrete.

int i = 1; runs once, before the loop does anything else.

i <= 3 is checked: 1 <= 3 is true, so the body runs, printing 1.

i++ runs, making i 2.

i <= 3 is checked again: 2 <= 3 is true, so the body runs, printing 2.

i++ runs, making i 3.

i <= 3 is checked again: 3 <= 3 is true, so the body runs, printing 3.

i++ runs, making i 4.

i <= 3 is checked one more time: 4 <= 3 is false, so the loop stops without running the body again.

The output is 1 2 3 — note that the update and the false condition check both still happen after the last real pass, even though nothing more gets printed.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 3; i++) {
        printf("%d ", i);
    }
    return 0;
}

Initialization can live outside the for header

Any of a for loop's three parts can be left empty — the semicolons themselves are what actually separate the parts, and they still have to be there even when a part has nothing in it. Moving the initialization out entirely and declaring the counter beforehand is perfectly legal.

i is declared and set to 1 before the loop begins; the header's first slot is simply left blank, but its semicolon still has to appear.

This is useful whenever the counter needs to be visible after the loop ends, or needs a value computed by something more involved than a plain literal — declaring int i inside the header would otherwise scope i to the loop alone.

#include <stdio.h>

int main() {
    int i = 1;
    for (; i <= 5; i++) {
        printf("%d\n", i);
    }
    return 0;
}

The update can live outside the for header too

The same trick works on the update slot: leave it empty in the header, and instead advance the counter by hand somewhere inside the body.

The trailing semicolon in the header still has to be there even with nothing after it — it's what tells C the update slot is intentionally empty, not a syntax error.

Once the update is moved into the body like this, it's entirely the programmer's job to make sure it actually runs on every pass — forgetting it here has exactly the same effect as forgetting it in a while loop's body, which is covered in detail later on this page.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 5;) {
        printf("%d\n", i);
        i++;
    }
    return 0;
}

The infinite for loop

Leaving all three parts of a for header empty is legal C, and it produces a loop with no built-in way to stop.

There's nothing in for (;;) itself that could ever end the loop — the only way out is the break statement, reached once the user types 0.

for (;;) isn't always a mistake. Plenty of real programs — a game's main loop, a server waiting for the next connection, an embedded system's control loop — are written to run indefinitely on purpose, exiting only through an explicit break, return, or a call that ends the program. The problem is only when a loop becomes infinite by accident, which is covered a little further down.

for (;;) {
    // runs forever unless something inside stops it
}
#include <stdio.h>

int main() {
    int command;

    for (;;) {
        printf("Enter 1 to continue, 0 to quit: ");
        scanf("%d", &command);
        if (command == 0) {
            break;
        }
    }
    return 0;
}

The while loop

A while loop has just one part — a condition — and nothing built in for initialization or updating. It keeps running its body for as long as that condition holds true.

Prints 1 through 5, exactly like the for-loop version earlier — the counter just has to be declared and advanced by hand since while's header doesn't do either automatically.

while (condition) {
    // body
}
#include <stdio.h>

int main() {
    int i = 1;
    while (i <= 5) {
        printf("%d\n", i);
        i++;
    }
    return 0;
}

How a while loop executes, step by step

A while loop's flow is the same check-then-run pattern as for, just without the automatic initialization and update wrapped around it.

Every single pass, including the very first one, starts with the condition being checked — the body never runs without that check happening first.

condition ------- false ------> loop ends
      |
     true
      |
      v
    body
      |
      `------------- back to condition

A while loop that executes zero times

Because the condition is checked before the body ever runs, a while loop can complete without running its body even once — if the condition is already false the first time it's tested, that's it.

i starts at 10, and 10 < 5 is false right away, so the body never runs at all — the program only ever prints "done".

Keep this example in mind — it's revisited a little later with a do-while loop built the exact same way, and the contrast between the two is the single biggest thing that separates them.

#include <stdio.h>

int main() {
    int i = 10;
    while (i < 5) {
        printf("%d\n", i);
        i--;
    }
    printf("done\n");
    return 0;
}

Why while suits condition-based repetition

while shines whenever the number of repetitions genuinely isn't known ahead of time — it only becomes clear as the program runs, based on something like user input.

There's no way to know in advance how many bad attempts a user will make before entering something valid — a for loop's fixed counter doesn't naturally fit here, but a while loop's open-ended condition does.

#include <stdio.h>

int main() {
    int num;

    printf("Enter a positive number: ");
    scanf("%d", &num);

    while (num <= 0) {
        printf("That is not positive, try again: ");
        scanf("%d", &num);
    }

    printf("Thanks, you entered %d\n", num);
    return 0;
}

The do-while loop

A do-while loop moves the condition check to the very end — the keyword do introduces the body, and the condition only appears afterward, next to while.

The semicolon after the closing while(condition) is required and is easy to forget, precisely because neither a plain for header nor a plain while header ends in a semicolon of its own. Leaving it off is a compile error, not a silent bug — but it catches almost everyone the first few times.

do {
    // body
} while (condition);

A basic do-while example

Prints 1 through 5, same as the for and while versions above — the body runs, then the condition i <= 5 decides whether to go around again.

#include <stdio.h>

int main() {
    int i = 1;
    do {
        printf("%d\n", i);
        i++;
    } while (i <= 5);
    return 0;
}

The one real difference: the body runs before anything is checked

Take the exact same setup from the while-loop example that ran zero times, and write it as a do-while instead.

i again starts at 10, and 10 < 5 is still false — but this time the body has already run once by the time that condition is even looked at, so the program prints 10 before stopping.

This is the entire distinction between while and do-while: with a plain while, a false starting condition means the body never runs at all, as seen earlier. With do-while, the body's first run is unconditional — the check only decides whether there's a second, third, or later pass.

#include <stdio.h>

int main() {
    int i = 10;
    do {
        printf("%d\n", i);
        i--;
    } while (i < 5);
    return 0;
}

while vs do-while, side by side

while can skip its body entirely; do-while's body always gets at least that first run before a check ever happens.

In one line: a while loop may execute its body zero or more times; a do-while loop always executes its body at least once, no matter what the condition says.

while:               do-while:
  check ---false--> skip body     run body
    |true                            |
    v                                v
  body                          check ---false--> stop
    |                                |true
    `-- back to check                `-- back to run body

A realistic do-while use case: a menu that must show at least once

A menu-driven program is the textbook place do-while feels natural — the menu has to be displayed before the user could possibly have made a choice, so there's no earlier point at which checking a condition would even make sense.

The menu prints, a choice is read, and only then is that choice checked — exactly matching the natural order a user actually experiences the program in.

Writing this same program with a plain while would need the menu-and-read code duplicated: once before the loop (so there's something to check the very first time) and again inside the loop body. do-while avoids that duplication precisely because its condition comes after the body.

#include <stdio.h>

int main() {
    int choice;

    do {
        printf("1. New Game\n2. Load Game\n3. Quit\n");
        printf("Choose an option: ");
        scanf("%d", &choice);

        if (choice < 1 || choice > 3) {
            printf("Invalid choice, try again.\n");
        }
    } while (choice < 1 || choice > 3);

    printf("You chose option %d\n", choice);
    return 0;
}

Nested loops

A loop can contain another loop inside its body. The inner loop runs through its entire cycle once for every single pass the outer loop makes — this is the standard way to work through anything naturally two-dimensional, like a grid of coordinates.

Prints every pairing of an outer value 1 to 3 with an inner value 1 to 3 — nine coordinate pairs in total, grouped three per line.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 3; i++) {
        for (int j = 1; j <= 3; j++) {
            printf("(%d,%d) ", i, j);
        }
        printf("\n");
    }
    return 0;
}

How a nested loop actually executes

Tracing the coordinate example above shows the inner loop restarting from scratch on every outer pass, not continuing where it left off.

Each time the outer loop advances, j is reinitialized to 1 and the inner condition j <= 3 starts being checked fresh — it's a completely new run of the inner loop, not a resumed one.

This multiplication is worth internalizing early: an outer loop of size m containing an inner loop of size n runs the innermost body m x n times total, not m + n. A loop nested three deep multiplies all three counts together, and it's easy to underestimate how fast that grows.

outer i=1: inner j runs 1,2,3 -> (1,1) (1,2) (1,3)
outer i=2: inner j runs 1,2,3 -> (2,1) (2,2) (2,3)
outer i=3: inner j runs 1,2,3 -> (3,1) (3,2) (3,3)

total iterations of the inner body = outer count x inner count = 3 x 3 = 9

Breaking out of a loop early

break exits the nearest enclosing loop immediately and completely — no further iterations run at all, even if the loop's own condition would otherwise still be true.

The loop header allows i up to 10, but the moment i reaches 5, break stops the loop right there — so the output is only 1 2 3 4, and 5 through 10 are never reached.

In a nested loop, break only exits the innermost loop it's written directly inside — it has no effect on any outer loop wrapped around it. Stopping an outer loop from inside a nested one usually means checking a flag variable in the outer loop's own condition.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 10; i++) {
        if (i == 5) {
            break;
        }
        printf("%d ", i);
    }
    return 0;
}

Skipping an iteration with continue

continue is less drastic than break — it skips only whatever's left of the current pass and jumps straight ahead to start the next one. The loop itself keeps running; only that one iteration's remaining work is abandoned.

When i is 3, continue skips the printf for that pass alone — every other value still prints, so the output is 1 2 4 5, with 3 simply missing rather than the loop stopping.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 5; i++) {
        if (i == 3) {
            continue;
        }
        printf("%d ", i);
    }
    return 0;
}

break vs continue, side by side

It's easy to reach for one when the other is actually meant. break answers "stop the whole loop now"; continue answers only "skip the rest of just this pass and carry on." Reading the intent in plain English before choosing between them is usually enough to avoid mixing them up.

break:     stop the loop entirely, right now
continue:  skip only the rest of this one pass, then keep looping

How continue behaves differently in a for loop versus a while loop

continue jumps to whatever comes next in the loop's own control flow — and that "next step" is different for for and while. In a for loop, continue still lets the update run before the condition is checked again, because the update is baked into the loop's own mechanics. In a while loop, there is no built-in update — continue jumps straight to the condition, skipping over anything below it in the body, including any manual update statement that happened to be written after the continue.

This is the same for-loop example from before: even on the pass where continue runs, i++ still fires afterward, because the update is part of the for header, not part of the body continue jumped out of.

This looks like the same idea rewritten as a while loop, but it isn't equivalent at all.

This while-loop version never finishes. The moment i reaches 3, continue jumps straight back to the condition i <= 5 — skipping the i++ that sits below it in the body. i is permanently stuck at 3, the condition stays true forever, and the loop spins endlessly. Whenever continue is used inside a while loop, any update the loop depends on must be placed before the continue can be reached, or the update needs to happen in a way continue can't skip over.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 5; i++) {
        if (i == 3) {
            continue;
        }
        printf("%d ", i);
    }
    return 0;
}
#include <stdio.h>

int main() {
    int i = 1;
    while (i <= 5) {
        if (i == 3) {
            continue;
        }
        printf("%d ", i);
        i++;
    }
    return 0;
}

The classic missing-update infinite loop

Beyond the continue trap above, the most ordinary version of this mistake is simply forgetting to write the update at all.

This loop never ends. Nothing inside the body ever changes i, so i <= 5 is checked against the same unchanged value forever and can never become false. The fix is just adding the missing update: while (i <= 5) { printf("%d\n", i); i++; } — every while loop needs something in its own body that moves it toward becoming false eventually.

#include <stdio.h>

int main() {
    int i = 0;
    while (i <= 5) {
        printf("%d\n", i);
    }
    return 0;
}

Off-by-one errors

Swapping < for <= (or the reverse) in a loop condition changes exactly how many times the body runs, and it's one of the single most common bugs in programming, in any language.

i takes the values 0, 1, 2, 3, 4 — five iterations in total.

Changing only < to <= makes i also take the value 5 — six iterations instead of five, from a single character.

This matters most around array bounds: for an array declared with 5 elements, valid indices only run 0 through 4. Writing i <= 5 as the loop condition reads or writes arr[5], one slot past the array's end — undefined behavior that can silently corrupt unrelated memory instead of failing loudly.

for (int i = 0; i < 5; i++) {
    printf("%d\n", i);
}
for (int i = 0; i <= 5; i++) {
    printf("%d\n", i);
}

Looping through an explicit numeric range

Counting up and counting down are both just a matter of which comparison and which update direction the loop uses.

Counts up from 10 to 20 inclusive, using <= and ++.

Counts back down from 20 to 10 inclusive — note that both the comparison (>= instead of <=) and the update direction (-- instead of ++) had to flip together; changing only one of them would make the loop run zero times or never end.

#include <stdio.h>

int main() {
    for (int i = 10; i <= 20; i++) {
        printf("%d ", i);
    }
    return 0;
}
#include <stdio.h>

int main() {
    for (int i = 20; i >= 10; i--) {
        printf("%d ", i);
    }
    return 0;
}

Using update expressions other than ++ and --

A for loop's update slot can be any valid expression, not just a plain increment or decrement — compound assignment operators work just as well.

i += 2 advances the counter by 2 each pass instead of 1, printing every even number from 0 to 20.

-= and *= work exactly the same way in the update slot — any expression that eventually pushes the condition toward false is fair game.

#include <stdio.h>

int main() {
    for (int i = 0; i <= 20; i += 2) {
        printf("%d ", i);
    }
    return 0;
}
for (int i = 100; i > 0; i -= 5) { /* counts down by 5s */ }
for (int i = 1; i <= 100; i *= 2) { /* doubles each pass: 1, 2, 4, 8... */ }

Multiple expressions in a for loop's init and update, via the comma operator

The comma operator lets more than one expression sit in a single header slot, evaluated left to right — this is how a for loop can track two counters moving toward each other at once.

i starts at 0 and climbs; j starts at 10 and falls; both move on every single pass because the comma operator runs i++ and j-- together as one update expression, and the loop stops the moment they meet in the middle.

#include <stdio.h>

int main() {
    for (int i = 0, j = 10; i < j; i++, j--) {
        printf("i=%d j=%d\n", i, j);
    }
    return 0;
}

Loop conditions follow the same truthy/falsy rule as if

A loop's condition doesn't have to be an explicit comparison — C treats any expression that evaluates to zero as false and anything nonzero as true, in a while or for condition exactly as it does in an if.

while (x) means "keep looping as long as x is nonzero" — it counts down 5, 4, 3, 2, 1, and stops the instant x reaches 0, because 0 itself is what while treats as false.

#include <stdio.h>

int main() {
    int x = 5;
    while (x) {
        printf("%d\n", x);
        x--;
    }
    return 0;
}

Choosing the right loop for the job

for reads best whenever a repetition is naturally tied to a counter with a known start, stop, and step — looping across a fixed range or a fixed-size array are the textbook cases, since the whole plan for the repetition fits in one header.

while reads best when the stopping point is something the program only discovers as it runs — reading input until a particular value shows up, or repeating until some condition elsewhere in the program changes, isn't naturally described by a counter set up in advance.

do-while is worth reaching for specifically when the body genuinely has to run before the first check would even make sense — a menu prompt or an input-validation loop are the recurring examples, since there's nothing yet to check until after the first read has actually happened.

A full side-by-side comparison

The two genuinely load-bearing rows are "condition checked" and "can run 0 times" — everything else about picking between the three loop forms follows from those.

for               while             do-while
condition checked   before body       before body       after body
can run 0 times     yes               yes               no
first run guaranteed no               no                yes
built-in init/update yes (in header)  no                no
best suited for     known iteration   condition-based    at-least-once
                    count / counting  repetition         work (menus,
                                                          validation)

The same counting task, written all three ways

To make the equivalence concrete, here's the exact same task — sum the numbers 1 through 5 — implemented with each loop form in turn.

for version — the counter's whole lifecycle lives in the header.

while version — same result, but the counter's declaration and update are both written by hand, outside any header.

do-while version — identical output again, since i starts at 1 and the condition is already going to be true on the first check anyway; the check-after-body difference only actually shows up when the starting condition would have been false.

All three print 15. The capability is identical — what differs between them is only how naturally each one's shape matches the way the task is being described.

#include <stdio.h>

int main() {
    int sum = 0;
    for (int i = 1; i <= 5; i++) {
        sum += i;
    }
    printf("%d\n", sum);
    return 0;
}
#include <stdio.h>

int main() {
    int sum = 0;
    int i = 1;
    while (i <= 5) {
        sum += i;
        i++;
    }
    printf("%d\n", sum);
    return 0;
}
#include <stdio.h>

int main() {
    int sum = 0;
    int i = 1;
    do {
        sum += i;
        i++;
    } while (i <= 5);
    printf("%d\n", sum);
    return 0;
}

A few more loop mistakes worth knowing

A handful of remaining mistakes don't attach as cleanly to any single section above, but are common enough to call out on their own.

Wrong boundary, one extra iteration: for (int i = 0; i <= n; i++) arr[i] = 0; run against an array declared as int arr[n]; walks one index past the array's last valid slot (n - 1), because <= n allows i to reach n itself. Whenever a loop's bound is a size like n rather than a literal count, double-check whether < n or <= n is actually correct for that specific array.

Looping in the wrong direction: for (int i = 10; i <= 20; i++) is meant to count down from 20 to 10 but keeps ++ instead of switching to i-- and >=. Since i starts at 10 and only grows, the condition i <= 20 stays true well past where the loop was meant to stop, producing far more iterations (or a different range entirely) than intended. Any time a loop's starting point is higher than its ending point, the update needs to move backward (i--), not forward.

Missing semicolon after do-while's condition: do { total += x; } while (x < limit) with no trailing semicolon fails to compile, because do-while is the one loop form in C whose closing line is itself a statement that has to be terminated like any other. for (...) { } and while (...) { } need no semicolon after their closing brace; do { } while (...); always does.

A for loop repeats a block a set number of times, typically counting with an index. A while loop repeats as long as a condition stays true, checked before each pass. A do-while loop is like while, but checks its condition after each pass, guaranteeing at least one execution.

Repetition is one of the two or three most fundamental things any program does (alongside branching and calling functions) — printing a table, processing every item in a list, or retrying an input until it's valid all rely on loops.

Processing every element of an array, reading input until a sentinel value appears, simulating time steps in a game or physics engine, and retry logic anywhere input might be invalid all use loops constantly.

A for loop is like being told 'do 10 push-ups' — the count is known up front. A while loop is like 'keep running until you're tired' — you don't know in advance how many steps it'll take, only the stopping condition. A do-while is 'take at least one sip of water, then keep drinking while you're still thirsty' — the first action always happens before the condition is even checked.

A for loop printing squares Prints the squares of 1 through 5, using a for loop's three-part header to initialize, test, and advance a counter.

int i = 1; runs once, before the loop starts at all — this is the initialization part of the for header.

i <= 5 is checked before every pass: since 1 <= 5 is true, the loop body runs.

printf("%d ", i * i) prints 1 (1 squared), followed by a space.

i++ runs after the body, incrementing i to 2 — this is the update part of the for header.

The condition i <= 5 is checked again with the new value; this repeats — printing 1 4 9 16 25 — until i becomes 6, at which point 6 <= 5 is false and the loop stops.

while + break + continue together Combines a while loop with continue (skip this iteration) and break (stop the loop entirely) to print only odd numbers up to 7.

Each pass increments i first, then checks: if i is even, continue immediately jumps back to the while condition, skipping the rest of that iteration's body (so nothing prints for even i).

If i is odd and greater than 7 (i.e. i becomes 9), break exits the loop immediately and completely — no further iterations happen at all, even though i < 10 is still true.

Only odd values 1, 3, 5, 7 actually reach the final printf before the loop is broken out of at i = 9.

for (int i = 0; i <= 5; i++) { arr[i] = 0; // arr has 5 elements, valid indices 0..4 } for (int i = 0; i < 5; i++) { arr[i] = 0; }

<= 5 runs the loop body for i = 0 through i = 5 — six iterations — writing one element past the end of a 5-element array on the last pass. Looping while i < 5 gives exactly the 5 valid indices.

int i = 0; while (i < 5) { printf("%d\n", i); } int i = 0; while (i < 5) { printf("%d\n", i); i++; }

Forgetting to update i inside the loop body means the condition i < 5 is always checked against the same unchanged value — this never becomes false, producing an infinite loop.

for's header has three parts separated by semicolons: initialization; condition; update — any of the three can be left empty (for(;;) is a valid, deliberately infinite loop), though that's uncommon outside specific patterns.

break immediately exits the nearest enclosing loop (or switch) entirely; continue skips the rest of the current iteration's body and jumps straight to the next iteration's condition check.

A while loop's condition is checked before the body ever runs, so its body can execute zero times if the condition starts false. A do-while's body always runs at least once, since the condition is only checked afterward.

Nested loops (a loop inside another loop) are how you process two-dimensional data, like a grid or a matrix — the inner loop completes fully for every single iteration of the outer loop.

Writing an off-by-one error in a loop condition — using < instead of <= (or vice versa) is one of the single most common bugs in all of programming, in every language, not just C.

Accidentally creating an infinite loop by forgetting to update the loop's controlling variable inside a while loop's body.

Placing a stray semicolon right after a for or while header — for (int i = 0; i < 10; i++); { printf("%d\n", i); } runs the empty statement 10 times and then the braced block exactly once, with i already at 10, not what was intended.

Using a do-while when a plain while was actually appropriate, executing a body at least once even when the initial condition was already false — worth double-checking whenever 'at least once' isn't actually a requirement.

Choosing for when the number of iterations is known or naturally counted (looping over an array's fixed length); choosing while when the stopping condition depends on something discovered during the loop (like reading input until EOF).

Using a do-while specifically for input-validation menus, where you genuinely want the prompt to show at least once before checking whether the input was valid.

Adding a defensive maximum-iteration counter or similar safeguard in any while loop whose exit condition depends on external input, to avoid a truly infinite loop in production code.

Loop performance: cache behavior and access patterns In nested loops over multidimensional arrays, the order in which you iterate (row-major vs. column-major) can change performance dramatically due to CPU cache behavior — iterating in the same order the data is actually laid out in memory (rows, for a standard C 2D array) is significantly faster than jumping around unpredictably, a topic developed further in the future Memory & Program Execution track.

Loop unrolling as a compiler optimization Modern optimizing compilers can automatically 'unroll' a loop with a known, small iteration count — replacing the loop entirely with repeated inline copies of its body — trading code size for fewer branch instructions and better instruction-pipeline behavior. This connects directly to the Compiler Optimization concepts covered in the Compiler Internals track.

What's the key difference between while and do-while?

while checks its condition before the first iteration, so its body might never run. do-while checks its condition after each iteration, guaranteeing the body runs at least once.

What does continue do differently from break inside a loop?

break exits the loop entirely, skipping any remaining iterations. continue only skips the rest of the current iteration's body and moves straight to the next iteration's condition check — the loop itself keeps going.

What's the classic C off-by-one bug in a for loop over an array of size n?

Using i <= n instead of i < n as the loop condition, which accesses index n — one past the array's valid range of 0 to n-1 — reading or writing out of bounds.

Why does a do-while loop guarantee at least one execution of its body, when a for loop or a while loop cannot make that same guarantee?

for and while both check their condition before the body ever runs, so if that condition is false on the very first check, the body is skipped entirely. do-while moves the check to after the body, so the body's first run happens unconditionally — the check only decides whether there's a second run, not whether the first one happens.

Why does continue behave differently inside a while loop compared to inside a for loop?

In a for loop, the update expression lives in the header and still runs after continue, right before the condition is checked again. In a while loop there is no built-in update — continue jumps straight to the condition, skipping over anything left in the body, including a manual update statement written after it. If that update was the loop's only way of progressing, the loop can end up stuck forever.

When is a do-while loop the natural choice over a for or a while loop?

When the body genuinely needs to run before there's anything meaningful to check — a menu that has to display before a choice exists to validate, or a prompt that has to run before there's input to check against a condition. In both cases there's no earlier point where a check-first loop could even test something useful.

What makes a for loop especially well suited to counting tasks?

A for loop's header holds the counter's initialization, its stopping condition, and its update all together in one place, right where the loop begins. That makes the entire plan for how many times the loop runs and how the counter moves visible at a glance, which is exactly the information a counting task revolves around.

Why does a missing update expression cause an infinite loop?

A loop only stops once its condition becomes false, and a condition that tests a variable can only change its answer if that variable's value changes between checks. If nothing in the loop's body (or header) ever updates that variable, the same value gets tested against the same condition forever, and the result never changes — so the loop can never exit on its own.

Write a C program to print all even numbers from 2 to 20 (inclusive) using a for loop.

Starting the counter at 2 and stepping by += 2 each pass lands on every even number automatically, so there's no need for an if check inside the body to filter out the odd ones.

Write a C program to calculate the factorial of a number entered by the user using a for loop.

factorial has to start at 1, not 0, since anything multiplied by 0 stays 0 forever; the loop then multiplies it by every whole number from 1 up to n in turn, which is exactly what a factorial is.

Write a C program to print a 5-by-5 multiplication table using nested for loops.

The outer loop picks a row number i, and for each row the inner loop runs completely, filling that row with i times every column number j from 1 to 5 — the inner loop's counter starts over from 1 on every single outer pass, and %4d keeps the columns aligned.

Write a C program that keeps asking the user for a number until they enter a positive one, using a do-while loop.

do-while fits naturally here because the prompt has to be shown at least once before there is any number to check at all — the loop only repeats when the value just read turns out to be zero or negative.

Write a C program to reverse a number (for example, 123 becomes 321) using a while loop.

Each pass peels off num's last digit with %10, appends it onto reversed (which shifts reversed's existing digits one place left first via * 10), then removes that digit from num with integer division by 10 — the loop naturally stops once num has been whittled all the way down to 0.

Write a C program to check whether a number is prime using a for loop.

Only divisors up to the square root of n need checking, since any factor larger than that would have to pair with one already found below it; break exits the loop the moment a divisor turns up, since one is all it takes to disprove primality.

Write a C program to calculate the sum of the digits of a number using a while loop.

The same peel-off-the-last-digit pattern used for reversing a number works here too — %10 isolates the last digit each pass, it gets added into a running sum, and /= 10 removes it so the next digit is ready for the following pass.

Write a C program to print the first n terms of the Fibonacci series using a for loop.

At every step, a is the term actually being printed, and b is already sitting one step ahead of it; after printing, both variables slide forward together (a becomes the old b, b becomes their sum) so the next iteration finds the same two-variable setup ready to go.

Write a C program that reads numbers from the user and adds them up, stopping as soon as the user enters 0, using a while loop.

This is a good fit for while rather than for because there's no way to know in advance how many numbers the user will enter before typing 0 — the read-and-prompt code has to appear once before the loop, so there's a first value to check, and again inside it for every value after that.

Write a C program to print a right-angled triangle pattern of stars, n rows tall, using nested for loops.

The outer loop counts the rows from 1 to n, and the inner loop's own upper bound is i rather than a fixed number — so row 1 prints one star, row 2 prints two, and so on, which is exactly what makes the shape grow one star wider on every row.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 5; i++) {
        printf("%d ", i * i);
    }
    printf("\n");
    return 0;
}
#include <stdio.h>

int main() {
    int i = 0;
    while (i < 10) {
        i++;
        if (i % 2 == 0) continue;   // skip even numbers
        if (i > 7) break;           // stop once past 7
        printf("%d ", i);
    }
    printf("\n");
    return 0;
}