Loops, updates and stopping
What happens after the last square is added?
This program adds squares. Predict both sum and i at printf. In particular, decide whether i is still 4 when the loop finishes.
#include <stdio.h>
int main(void)
{
int sum = 0;
int i;
for (i = 1; i <= 4; i++) {
sum += i * i;
}
printf("%d %d\n", sum, i);
return 0;
}A loop repeats a body while its condition allows another pass. Each completed pass is an iteration. You already know how to read an assignment and interpret a zero/nonzero condition. Keep those same rules: each new test and update reads the current state, not the starting state. This lesson uses C11, initialized-before-read int values and intermediate results within −32767…32767. All displayed complete programs can be compiled separately.
Read the four stages of for
In for (i=1; i<=4; i++), the first part sets the starting state, the middle part is a condition, and the last part is an update. The two semicolons separate those parts; they do not mean all three happen before the body.
- Initialization: execute i=1 once when the loop is reached.
- Test: evaluate i<=4 using the current i. If false, leave the loop immediately.
- Body: if the test was true, run the braced statements in order.
- Update: after this body completes, execute i++, then return to step 2, not step 1.
Here i is declared before the loop so it is still available to printf afterwards. Its declaration int i; does not give it a usable starting value, but the for initialization assigns 1 before any read. The earlier sum=0 establishes an accumulator: a variable that retains a running total between iterations. sum += i*i means compute the square using the current i, then add it to the current sum. Do not reset sum to zero inside the body; doing so would discard earlier contributions.
The update i++ is separate from the body's arithmetic. Its old expression value is unused; its important effect here is to increase i by 1. The final update still happens after the final successful body. Only the next test discovers that no more body should run.
Source rule: the order of the for stages is specified in C11 draft N1570, §6.8.5.3p1. Its loop stopping rule is §6.8.5p4.
Worked example 1: total, body count and final state
- Before the first test: sum=0 and initialization has set i=1.
- Test 1: 1<=4 is true. Body: i*i=1; sum=0+1=1. Update: i=2.
- Test 2: 2<=4 is true. Body: i*i=4; sum=1+4=5. Update: i=3.
- Test 3: 3<=4 is true. Body: i*i=9; sum=5+9=14. Update: i=4.
- Test 4: 4<=4 is true. Body: i*i=16; sum=14+16=30. Update: i=5.
- Test 5: 5<=4 is false. Do not run the body or another update. Exit with sum=30, i=5.
- Output: 30 5, followed by a newline.
There is one initialization, five condition evaluations, four body executions and four updates. The body sees i=1,2,3,4; the condition also sees the stopping value 5. The pair 30 4 misses the last update. The pair 55 5 wrongly adds the square of 5 even though its test fails.
Why the accumulator is correct and why the loop stops
A loop invariant is a statement about the state that remains true at a chosen checkpoint. Here choose “just before every test”: sum contains the squares of all positive integers already visited, from 1 through i−1. This is an ordinary description of what the variable means, not a new statement added to the code.
- At the first test, i=1 and no terms have been visited, so the total is 0. The statement holds before any body runs.
- If the statement holds and i<=4 is true, the body adds exactly the next square i*i. The update moves i to the next integer, so before the next test the total again covers exactly 1 through i−1.
- On exit i=5, so the preserved statement says sum covers 1 through 4. This agrees with 1+4+9+16=30.
Correct accumulation and stopping are separate questions. This loop stops because i starts at 1 and increases by 1 on each completed pass until it becomes 5. Its test then fails. Every visited value, product and total is small and representable. A condition alone does not force progress: if the needed update is missing, you cannot assume the condition eventually becomes false. We only trace finite, safely bounded loops here.
while checks first; the body supplies the progress
A while loop has the form while (condition) followed by a body. Test the condition before every possible body execution. If false initially, the body runs zero times. A while statement has no automatic update field: the statements inside its body must change the state when progress requires it.
Worked example 2: halve an integer on every pass
#include <stdio.h>
int main(void)
{
int n = 41;
int steps = 0;
while (n > 3) {
n = n / 2;
steps++;
}
printf("%d %d\n", n, steps);
return 0;
}- Start: n=41, steps=0.
- Test 1: 41>3 is true. Body: n=41/2=20; then steps becomes 1.
- Test 2: 20>3 is true. Body: n=20/2=10; then steps becomes 2.
- Test 3: 10>3 is true. Body: n=10/2=5; then steps becomes 3.
- Test 4: 5>3 is true. Body: n=5/2=2; then steps becomes 4.
- Test 5: 2>3 is false. Exit without another division or increment.
- Output: 2 4, followed by a newline.
The body runs four times; the condition is evaluated five times. n is assigned four times by the body, and steps++ runs four times. Do not invent a fifth update after the final failed test. There is no separate for-style update stage in this while loop.
At each test, steps counts completed halvings and n is the integer obtained from the starting 41 by exactly those halvings. The description is true for zero steps; each body carries out one integer division and increments steps once, so it stays true. Integer division is performed on every pass: 41/2 is 20, and 5/2 is 2. Keeping 20.5 or 2.5 would trace a different kind of arithmetic.
For termination, while n>3, n is an integer at least 4. Dividing it by 2 gives a nonnegative integer strictly smaller than n. A strictly decreasing nonnegative integer cannot keep decreasing forever, so it eventually reaches a value at most 3 and the next test fails. This reasoning uses both the test and the update. With a starting n of 3 or 0, the first test is already false: zero bodies, one test, and steps remains 0. Source rules: N1570, §6.8.5.1p1 and integer division §6.5.5p6.
Empty ranges and the post-test alternative
A for or while loop can have an empty set of body executions. For example, start a for loop at i=6 and use i<=4: initialization still sets i to 6, the first test fails, and neither body nor update runs. The accumulator keeps its starting value. “A loop must run once to test its condition” is false for these pre-test forms.
A do...while loop puts the condition after the body. Its sequence is body → test → body → test, until a test fails. There is a semicolon after its closing while(condition). With normal entry and completion of the body, the body runs once before any test, even when the corresponding initial pre-test condition would be false. Practice question 3 below compares this boundary directly. See N1570, §6.8.5.2p1.
For the finite loops in this lesson, which leave only because the condition becomes false, use these counts:
- for: one initialization; k bodies and k updates; k+1 tests, including the last failed test. If k=0, there is one test and no updates.
- while: k bodies and k+1 tests. Count assignments and increments from its body; there is no implicit update.
- do...while: k bodies and k tests, with k at least 1 in the examples here. Each completed body is followed by one test, including the final false one.
These are counts of the source program's logical steps. They do not claim a particular number of machine instructions or physical tests after compiler optimization. The counts assume the simple normal flow shown here; early exits and other control-flow features are outside this lesson.
Practice: trace before checking the answers
These four questions are unscored. Record tests, bodies and updates separately rather than looking only at the last printed value.
Practice question 1: an update larger than one
List the i values seen by the body, the final sum and final i. Count the condition evaluations and updates.
#include <stdio.h>
int main(void)
{
int sum=0,i;
for(i=2;i<9;i+=3){sum+=i;}
printf("%d %d\n",sum,i);
return 0;
}Practice question 2: count integer divisions
How often does the body execute? Give count and n at the end, and include the final failed test in your trace.
#include <stdio.h>
int main(void)
{
int n=30,count=0;
while(n>0){n=n/3;count++;}
printf("%d %d\n",count,n);
return 0;
}Practice question 3: first test or first body?
Compare the two loops. Give a and b at the end, with the number of bodies and tests for each.
#include <stdio.h>
int main(void)
{
int a=0,b=0;
while(a<0){a++;}
do{b++;}while(b<0);
printf("%d %d\n",a,b);
return 0;
}Practice question 4: choose the inclusive stopping condition
Fill the test in for(i=1; ___; i++) so that the body sum+=i adds exactly 1 through 5. Give the final total and counts. Assume sum begins at 0 and the body contains only sum+=i.
Practice solutions and wrong-answer feedback
Practice question 1 solution
- Start with sum=0; initialization sets i=2.
- Test 2<9 is true. Body adds 2: sum=2. Update adds 3: i=5.
- Test 5<9 is true. Body adds 5: sum=7. Update: i=8.
- Test 8<9 is true. Body adds 8: sum=15. Update: i=11.
- Test 11<9 is false. Exit. Output: 15 11, then a newline.
The body visits 2,5,8. There are three bodies, three updates and four tests. A final i of 8 omits the last update. A final i of 9 assumes the loop stops exactly on its bound, but the update jumps from 8 to 11. sum=26 wrongly includes 11 after its failed test. The invariant is that sum contains exactly the visited terms before the current i; positive increments of 3 eventually move i beyond the bound.
Practice question 2 solution
- Start: n=30, count=0. Test 30>0 is true.
- First body: n=30/3=10, count=1. Next test 10>0 is true.
- Second body: n=10/3=3, count=2. Next test 3>0 is true.
- Third body: n=3/3=1, count=3. Next test 1>0 is true.
- Fourth body: n=1/3=0, count=4. Final test 0>0 is false.
There are four bodies and five tests; output is 4 0, then a newline. Three bodies is wrong because n=1 still satisfies n>0. Five bodies counts the final failed test as a body. A fractional final n is wrong because both operands of each division are int. While n>0, division by 3 strictly decreases this positive integer, including the step 1 to 0, so the loop stops. count records exactly how many divisions have completed.
Practice question 3 solution
- Start: a=0, b=0.
- The while test a<0 is 0<0, false. Its body runs zero times; its test runs once; a remains 0.
- The do body runs before its first test. b++ changes b from 0 to 1.
- Its test b<0 is now 1<0, false. The do loop has one body and one test.
- Output: 0 1, followed by a newline.
The pair 0 0 incorrectly tests the do condition before its body. The pair 1 1 incorrectly grants the while loop an initial body execution. The do test reads the updated b=1, not the starting b=0. No extra update happens after either failed test.
Practice question 4 solution
#include <stdio.h>
int main(void)
{
int sum=0;
for(int i=1;i<=5;i++){sum+=i;}
printf("%d\n",sum);
return 0;
}The declaration int i=1 in the for initializer supplies the starting value. This i belongs to the loop and is not available after it; printf therefore prints only sum. The next lesson returns to local variable scope.
Use i<=5; i<6 is also correct for this integer sequence. The body sees i=1,2,3,4,5 and the running sum becomes 1,3,6,10,15. After the fifth body, i++ makes the loop's i equal to 6. The sixth test fails, so the output is 15 and a newline. There is one initialization, five bodies, five updates and six tests.
The condition i<5 omits the term 5 and produces 10. The condition i<=6 includes an unwanted 6 and produces 21. A proposal that only happens to print 15 but visits other terms does not meet “exactly 1 through 5.” The accumulator begins at 0 and never resets. Before each test it equals the sum of positive integers less than the current i; when the failing test is reached, the intended five terms have been added.
Carry a loop into a function
You can now distinguish the last successful body from the final failed test, describe what an accumulator means, and explain why a small loop stops. Next, place a finite loop inside a function and track its local variables separately from the caller's variables.
Analogy
Picture adding the prices of a short list to a running total. Before each addition, a marker says which item is next. A for loop first places the marker, checks whether that item belongs to the list, adds its price, and moves the marker. After the last addition the marker moves past the list; the next check stops the work. That is why the final marker position differs from the last position used for an addition.
The running total remembers earlier items. Clearing it before every new item would keep only the last price. A while loop is similar but leaves the marker movement to the body. A do...while loop is like doing one action before checking whether to repeat; use it only when that initial action is wanted. The analogy describes source-level order, not processor timing.
Quick reference
- for: initialization once → test → body → update → test again.
- A false for test skips both the next body and its update. A last successful body still gets its update.
- while: test → body → test. Any progress update must be supplied by the body or condition; our examples keep updates in the body.
- do...while: body → test. The closing while(condition) is followed by a semicolon.
- For normal finite loops that exit only on a false condition: for has k bodies, k updates and k+1 tests; while has k bodies and k+1 tests; do...while has k bodies and k tests, k>=1.
- A pre-test loop can have zero bodies and one test. An initially empty range leaves the accumulator unchanged.
- Initialize an accumulator before the loop; add to its current value rather than resetting it each pass.
- State an invariant at one checkpoint, then check its starting truth and how a body/update preserves it.
- Explain progress toward a failing condition separately from correct accumulation. Check intermediate arithmetic stays representable.
- Report body-visited values, the stopping value, body/test/update counts, and the final state. Source-step counts are not machine-instruction counts.
Notes for this lesson
Sign in to keep your progress. Sign in