Loops in C: for, while and do-while with Tested Programs (C Learning Guide, Part 4)
A program that asks once can still be stopped by one wrong answer. A loop lets C repeat a piece of work: pack order after order in a single run, and keep asking a question until the answer is valid. This part adds loops to the packing tool, so one run handles many orders and never divides by a bad capacity.
This is Part 4 of the C Learning Guide series. Part 1 built the first working packing program, Part 2 added variables, types, printf and scanf, and Part 3 taught the tool to refuse bad input with if and else. Every program below was compiled and run for these notes; the outputs shown are the real outputs.
1. Repeating work: the while loop
A while loop repeats a block of code as long as its condition is true. C checks the condition before each pass. If the answer is yes, the block between the braces runs once, and then C goes back and checks again. If the answer is no, C skips the block and continues after it. When the condition is already false at the start, the block never runs at all.
Packing is a natural loop: while items remain, fill the next box.
#include <stdio.h>
int main(void) {
int items = 17;
int capacity = 6;
int box = 1;
int take;
while (items > 0) {
take = capacity;
if (items < capacity) {
take = items;
}
printf("Box %d: packed %d items\n", box, take);
items = items - take;
box = box + 1;
}
printf("All items packed\n");
return 0;
}
Line by line: items > 0 asks whether anything is still unpacked. Inside the loop, take is the box capacity, or fewer when the last box is not full. The two lines after the printf are what make the loop finish: items = items - take shrinks the remaining work, and box = box + 1 counts the box just filled. When items reaches 0, the condition fails and the loop ends.
Verified output:
Box 1: packed 6 items
Box 2: packed 6 items
Box 3: packed 5 items
All items packed
Seventeen items and a capacity of six fill two full boxes and a third box with five. Change items to 6 and exactly one box prints; change it to 0 and only the final line prints, because the condition is false on the very first check.
- Q: What happens if the condition is false at the start?
- The body runs zero times. C checks before the first pass, so a while loop can do nothing at all, and that is often the correct result, such as zero items to pack.
- Q: What makes a while loop stop?
- Something inside the loop must move the condition towards false. Here, items shrinks on every pass. If nothing changes the values in the condition, and no break or return leaves early, the loop runs forever.
- Q: Can a while loop replace an if statement?
- No. An if runs its block at most once. A while keeps running its block until the condition fails.
2. The for loop: counted repetition
A for loop is a while loop with its three control parts written together on one line: for (start; check; step). The start runs once before the first pass, the check is tested before each pass exactly like a while condition, and the step runs after each pass. Reach for for when you know how many passes you need, such as one pass per box.
#include <stdio.h>
int main(void) {
int boxes = 4;
int capacity = 6;
int total = 0;
int i;
for (i = 1; i <= boxes; i = i + 1) {
total = total + capacity;
printf("After box %d: %d items packed\n", i, total);
}
printf("Total capacity: %d items\n", total);
return 0;
}
Reading the for line: i = 1 starts the counter at box 1, i <= boxes keeps the loop going while boxes remain, and i = i + 1 advances to the next box after each pass. Inside, total grows by one capacity per pass, so the printf reports progress after every box.
Verified output:
After box 1: 6 items packed
After box 2: 12 items packed
After box 3: 18 items packed
After box 4: 24 items packed
Total capacity: 24 items
Four boxes at six items each give twenty-four. The same loop with i < boxes would stop after three boxes: the bounds decide the count, so read them carefully.
- Q: Is for the only way to count in C?
- No. A while loop with a counter does the same job. for simply keeps the start, check and step in one place so the counting is easy to see and hard to forget.
- Q: Which of the three parts can be left out?
- Any of them, because each is optional, but the two semicolons must stay. Leaving out the step is a common way to build an accidental endless loop, so leave parts out only with a reason.
- Q: Can the counter start at 0?
- Yes. Starting at 0 and checking
i < boxesalso gives exactly four passes. Pick the bounds that match how you are counting, and test with a small number.
3. do-while: run first, check after
A do-while loop runs its block once and only then checks the condition. If the condition holds, the block runs again; if not, the loop ends. Use it when the work must happen at least once before any check makes sense. Asking a question is the classic case: you must ask once before you can judge the answer.
Part 3 refused an invalid capacity and stopped. With do-while, the tool can ask again instead of giving up:
#include <stdio.h>
int main(void) {
int capacity = 0;
int ok;
do {
printf("Capacity per box (at least 1): ");
ok = scanf("%d", &capacity);
if (ok != 1) {
printf("Not a number. Stop.\n");
return 1;
}
if (capacity < 1) {
printf("Capacity must be at least 1.\n");
}
} while (capacity < 1);
printf("Capacity set to %d\n", capacity);
return 0;
}
Line by line: the block asks for a capacity and reads it with scanf. A non-number still stops the program, because there is nothing safe to retry with. A number below 1 prints the complaint, and the condition capacity < 1 sends the program back to ask again. Only a capacity of at least 1 lets the loop end.
Verified output, answering 0 and then 6:
Capacity per box (at least 1): Capacity must be at least 1.
Capacity per box (at least 1): Capacity set to 6
Verified output, answering with text:
Capacity per box (at least 1): Not a number. Stop.
- Q: How is do-while different from while?
- The check moves from before the block to after it. A while loop can run zero times; a do-while always runs at least once.
- Q: Why not ask again with a plain while?
- You would need the question written twice: once before the loop to get a first answer, and once inside to get the next. do-while writes the question once, which is why it fits ask-and-retry work.
4. break and continue: leave early, skip a turn
break ends the loop immediately, even when its condition is still true. continue skips the rest of the current pass and jumps straight to the next check. Together they handle the two interruptions in loading a truck: stop when the truck is full, and skip a damaged item without stopping everything.
#include <stdio.h>
int main(void) {
int capacity = 10;
int loaded = 0;
int w;
printf("Enter item weights one per line, 0 to stop:\n");
while (1) {
if (scanf("%d", &w) != 1) {
printf("Not a number. Stop.\n");
return 1;
}
if (w == 0) {
break;
}
if (w < 0) {
printf("Negative weight skipped\n");
continue;
}
if (loaded + w > capacity) {
printf("Item of weight %d does not fit; truck is full\n", w);
break;
}
loaded = loaded + w;
printf("Loaded %d, total %d of %d\n", w, loaded, capacity);
}
printf("Final load: %d of %d\n", loaded, capacity);
return 0;
}
Line by line: while (1) loops forever on its own, because 1 is always true, so the exits inside do the real work. A weight of 0 is the agreed stop signal, so break leaves the loop. A negative weight cannot go on a truck, so continue skips it and asks for the next one. An item that would overflow the remaining space prints a refusal and breaks, because the truck is now full. Anything else loads and reports the running total.
Verified output, entering the weights 4, 3, -2, 5, 2 and 0:
Enter item weights one per line, 0 to stop:
Loaded 4, total 4 of 10
Loaded 3, total 7 of 10
Negative weight skipped
Item of weight 5 does not fit; truck is full
Final load: 7 of 10
The 5 never loads: 7 plus 5 is over the capacity of 10, so the loop ends and the 2 is never even read. The final line reports what actually fit.
- Q: Does break end the whole program?
- No. It ends only the loop it sits in, and the program continues on the next line after the loop.
- Q: What does continue do in a for loop?
- It skips to the step and then the check, so counting still advances. In a while or do-while it jumps straight to the check, so be sure the condition can still change.
- Q: Is while (1) with break bad style?
- It is common when the stop point belongs in the middle of the work, like reading until a stop signal. What matters is that every path through the loop reaches a clear exit.
5. Choosing the right loop
All three loops can express any repetition, but each one makes one shape of work read most clearly:
| Loop | Condition checked | Runs at least once? | Reach for it when |
|---|---|---|---|
| for | Before each pass | No | You know the number of passes, such as one per box or the numbers 1 to 10. |
| while | Before each pass | No | You repeat until a condition changes and do not know the count in advance, such as packing until nothing remains. |
| do-while | After each pass | Yes, at least once | The work must happen once before any check, such as asking a question again until the answer is valid. |
Whichever loop you pick, three rules keep it honest:
- the condition must moveUnless you leave early with break or return, something inside the loop must change what the condition checks, or the loop never ends. A deliberate while (1) loop with a planned break inside is the intentional exception.
- before or afterDecide whether zero passes or one guaranteed pass is the right behaviour, and pick the loop that gives it.
- validate before you countRefuse bad input before the loop uses it in a calculation, exactly as Part 3 taught.
6. The full packing tool with loops
The tool now packs many orders in one run. Each order repeats Part 3's checks, the capacity question repeats until the answer is valid, and a running total counts every box used. Typing 0 items is the agreed stop signal.
#include <stdio.h>
int main(void) {
int items;
int capacity = 0;
int full;
int left;
int used;
int orders = 0;
int total_boxes = 0;
while (1) {
printf("\nItems in this order (0 to finish): ");
if (scanf("%d", &items) != 1) {
printf("Not a number. Stop.\n");
return 1;
}
if (items == 0) {
break;
}
if (items < 0) {
printf("Items cannot be negative. Order skipped.\n");
continue;
}
do {
printf("Capacity per box (at least 1): ");
if (scanf("%d", &capacity) != 1) {
printf("Not a number. Stop.\n");
return 1;
}
if (capacity < 1) {
printf("Capacity must be at least 1.\n");
}
} while (capacity < 1);
full = items / capacity;
left = items % capacity;
used = full;
if (left > 0) {
used = used + 1;
}
printf("Full boxes: %d, leftover items: %d, boxes used: %d\n", full, left, used);
orders = orders + 1;
total_boxes = total_boxes + used;
}
printf("Orders packed: %d\n", orders);
printf("Boxes used in all: %d\n", total_boxes);
return 0;
}
How it fits together: the outer while (1) is the order loop, ended by break on the stop signal. A negative item count is skipped with continue, so one bad order does not kill the run. The inner do-while is the ask-again loop from section 3. The arithmetic is Part 1: full boxes come from division, leftovers from the remainder, and a leftover means one more box. Everything is validated before any division, so a zero capacity can never reach the division line.
Verified output, packing orders of 17 items, then 9 items with a refused capacity of 0, then 5 items:
Items in this order (0 to finish): Capacity per box (at least 1): Full boxes: 2, leftover items: 5, boxes used: 3
Items in this order (0 to finish): Capacity per box (at least 1): Capacity must be at least 1.
Capacity per box (at least 1): Full boxes: 3, leftover items: 0, boxes used: 3
Items in this order (0 to finish): Capacity per box (at least 1): Full boxes: 0, leftover items: 5, boxes used: 1
Items in this order (0 to finish): Orders packed: 3
Boxes used in all: 7
Three orders used seven boxes in all. Notice the middle order: the refused capacity asks again inside the same order, and the run continues instead of stopping.
7. Wrong turns with loops
Three mistakes cause most beginner loop bugs, and each has a signature you can spot in seconds.
The condition never changes. This loop prints box numbers forever, because items is never reduced:
while (items > 0) {
printf("Box %d packed
", box);
box = box + 1;
/* items never changes: this loop never ends */
}
If a test run does not stop, look first for the line that should move the condition. Here it is the missing items = items - take.
The stray semicolon. A semicolon right after the condition ends the loop with an empty body, so the real block is no longer the loop at all:
int i = 1;
while (i <= 5); /* the semicolon ends the loop here */
{
printf("%d
", i); /* this block is not the loop body */
i = i + 1;
}
This program prints a single 1, or hangs forever, depending on the compiler, and the extra semicolon is the whole cause. C allows an empty statement, so the compiler accepts it without a word.
Off by one. for (i = 1; i < boxes; i = i + 1) with boxes set to 4 prints three labels, not four. When a loop runs once too often or once too rarely, count the passes on your fingers with a tiny number: 1, 2, 3, and the check fails at 4. Choosing <= or starting at 0 with < both fix it; mixing the two habits is what causes the bug.
- Q: How do I catch an endless loop while testing?
- Run with the smallest input that should finish, such as one box. If it does not stop, check that every pass changes a value in the condition.
- Q: Will the compiler warn me about these mistakes?
- Usually not. All three are legal C, which is why they survive compilation and only show up in runs. Testing with small, known inputs is the reliable defence.
8. C programs for practice with solutions
Practice 1: multiplication table. Read a number and print its table from 1 to 10. This is counted repetition, so a for loop fits.
#include <stdio.h>
int main(void) {
int n;
int i;
printf("Print the table of: ");
if (scanf("%d", &n) != 1) {
printf("Not a number.\n");
return 1;
}
for (i = 1; i <= 10; i = i + 1) {
printf("%d x %d = %d\n", n, i, n * i);
}
return 0;
}
Verified output, entering 7:
Print the table of: 7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70
Practice 2: count the digits. Read a non-negative whole number and print how many digits it has. Keep dividing by 10 while something remains: each division removes one digit. The count starts at 1 so that 0 itself is handled correctly.
#include <stdio.h>
int main(void) {
int n;
int digits = 1;
printf("Enter a whole number (0 or more): ");
if (scanf("%d", &n) != 1) {
printf("Not a number.\n");
return 1;
}
if (n < 0) {
printf("Negative numbers are not handled here.\n");
return 1;
}
while (n >= 10) {
n = n / 10;
digits = digits + 1;
}
printf("Digits: %d\n", digits);
return 0;
}
Verified output, entering 5042:
Enter a whole number (0 or more): Digits: 4
Practice 3: factorial with a guard. Read n and print n!, the product of all numbers from 1 up to n. Factorials grow very fast, so the program refuses values outside 0 to 12, where the answer stays small enough for a long variable. Recall from Part 2 that a long holds a wider range than an int. By definition, 0! is 1, and the loop naturally gives that answer because it never runs when n is 0.
#include <stdio.h>
int main(void) {
int n;
int i;
long fact = 1;
printf("Enter n (0 to 12): ");
if (scanf("%d", &n) != 1) {
printf("Not a number.\n");
return 1;
}
if (n < 0 || n > 12) {
printf("Keep n between 0 and 12 so the answer fits.\n");
return 1;
}
for (i = 2; i <= n; i = i + 1) {
fact = fact * i;
}
printf("%d! = %ld\n", n, fact);
return 0;
}
Verified output, entering 5:
Enter n (0 to 12): 5! = 120
What was checked, and what comes next
All eight programs in this part were compiled with a C17 compiler with warnings on, and every output block above is the captured output of a real run, including the refused inputs. Next in the series: functions - naming a piece of work once and calling it from many places, so the packing tool's checks are written once and used everywhere. Continue with the series index, or browse the Exam Notes.
Sources and scope
- cppreference: while statement for the condition checked before each pass.
- cppreference: for statement for the start, check and step parts.
- cppreference: do-while statement for the condition checked after each pass.
- cppreference: break and cppreference: continue for leaving a loop early and skipping a pass.
- cppreference: scanf for the return count of successful conversions.
Programs were tested on 64-bit Linux with gcc, where int is 32 bits and long is 64 bits. Results on other systems can differ for very large values.
Comments
Post a Comment