munotes®

while Loops

Chapter Twenty-Seven

Syllabus topic 1, "Control Flow: Statements and Blocks, If-Else, Else-If, Switch, Loops- While and For Loops Do-while, Break and Continue, Goto and Labels"

Pages 127 to 131 of 222

In one line

while (condition) statement tests the condition first and runs the statement again and again for as long as the condition stays non-zero, so a while loop may run zero times.

The form, and the three parts every loop needs

while (condition)
    statement

The condition is tested. If non-zero, the statement runs, and then the condition is tested again. When the test fails, control passes to whatever follows the loop.

Every correct loop has three things, and a loop that fails to end is always missing the third:

  1. Initialisation. Something set up before the loop.
  2. A condition that can become false.
  3. Progress, inside the body, towards making it false.
#include <stdio.h>

int main(void)
{
    int i = 1;                    /* 1. initialisation */

    while (i <= 5) {              /* 2. condition      */
        printf("%d squared is %d\n", i, i * i);
        i++;                      /* 3. progress       */
    }
    printf("the loop finished with i at %d\n", i);
    return 0;
}
1 squared is 1
2 squared is 4
3 squared is 9
4 squared is 16
5 squared is 25
the loop finished with i at 6

The loop ended with i at 6, not 5. It had to: the condition is tested with i at 6, fails, and only then does the loop stop. That off-by-one is the first thing to check when a loop gives an answer one too big or one too small.

It may run zero times

This is the property that distinguishes while from do-while, and it is usually what you want.

#include <stdio.h>

int main(void)
{
    int n = 0;
    int count = 0;

    while (count < n) {
        printf("this never prints\n");
        count++;
    }
    printf("with n = %d the body ran %d time(s)\n", n, count);
    return 0;
}
with n = 0 the body ran 0 time(s)

The condition was false before the first pass, so the body never ran. A loop that must run at least once is chapter 29.

The practical: reversing the digits of a number

MU's Practical 3(a), and her own words name the loop: "using while loop to reverse the digits of a number".

The method is two operators from chapter 15. n % 10 gives the last digit; n / 10 removes it. Do both until nothing is left, building the answer up as you go.

#include <stdio.h>

int main(void)
{
    int n;

    printf("Enter a whole number: ");
    if (scanf("%d", &n) != 1) {
        printf("\nThat was not a whole number.\n");
        return 1;
    }

    int original = n;
    int negative = n < 0;
    if (negative) {
        n = -n;
    }

    int reversed = 0;
    while (n > 0) {
        int digit = n % 10;
        reversed = reversed * 10 + digit;
        n = n / 10;
    }
    if (negative) {
        reversed = -reversed;
    }

    printf("\n%d reversed is %d\n", original, reversed);
    return 0;
}
munotes.in127

while Loops

5024
Enter a whole number:
5024 reversed is 4205

Trace it by hand, which is what an examiner will ask you to do at the board:

Passn at the startdigitreversed becomesn becomes
1502440 × 10 + 4 = 4502
250224 × 10 + 2 = 4250
350042 × 10 + 0 = 4205
455420 × 10 + 5 = 42050

n is 0, the condition fails, and reversed is 4205.

Three things in that program that a bare textbook version leaves out, and each is a viva question.

1. Zero. while (n > 0) with n of 0 runs zero times, and reversed stays 0, which is right.

2. A negative number. -5024 % 10 is -4 in C, because the remainder takes the sign of the left operand (chapter 15). Without the sign handling, the loop would never end, since n > 0 is false at once and the answer would be 0. The program takes the sign off, reverses, and puts it back.

3. A trailing zero is lost, and that is arithmetic, not a bug. 5024 reversed is 4205; 50240 reversed is 4205 as well, because 04205 is 4205. If the leading zero must be kept, the answer is a string and not a number.

The practical: the factorial

MU's Practical 3(b). The factorial of n is the product of every whole number from 1 to n, and 0 factorial is 1 by definition.

#include <stdio.h>

int main(void)
{
    int n;

    printf("Enter a whole number: ");
    if (scanf("%d", &n) != 1) {
        printf("\nThat was not a whole number.\n");
        return 1;
    }
    if (n < 0) {
        printf("\nThe factorial of a negative number is not defined.\n");
        return 1;
    }
    if (n > 20) {
        printf("\n%d! is too large for a 64-bit integer. The largest that "
               "fits is 20!.\n", n);
        return 1;
    }

    unsigned long long factorial = 1;
    int i = 1;
    while (i <= n) {
        factorial = factorial * i;
        i++;
    }

    printf("\n%d! = %llu\n", n, factorial);
    return 0;
}
12
Enter a whole number:
12! = 479001600

The n > 20 check is the part worth having. Factorials grow faster than anything else in a first-semester program, and a textbook solution using int gives silently wrong answers from 13 onwards. Here is the limit, measured:

#include <stdio.h>
#include <limits.h>

int main(void)
{
    printf("an int holds up to        %d\n", INT_MAX);
    printf("13! is 6227020800, which does NOT fit in an int\n");
    printf("an unsigned long long holds up to %llu\n", ULLONG_MAX);

    unsigned long long f = 1;
    for (int i = 1; i <= 21; i++) {
        f = f * (unsigned long long) i;
        if (i >= 19) {
            printf("%2d! = %llu%s\n", i, f, i == 21 ? "   <- WRONG, it wrapped" : "");
        }
    }
    return 0;
}
munotes.in128

while Loops

an int holds up to        2147483647
13! is 6227020800, which does NOT fit in an int
an unsigned long long holds up to 18446744073709551615
19! = 121645100408832000
20! = 2432902008176640000
21! = 14197454024290336768   <- WRONG, it wrapped

21 factorial does not fit in 64 bits, and unsigned arithmetic wraps rather than reporting anything (chapter 9). The answer printed for 21 is not the factorial of 21 and the program did not notice. That is why the check comes before the loop.

The infinite loop, and the four ways to write one by accident

while (1) { ... }        /* deliberate, and legitimate with a break inside */

The accidental ones:

int i = 1;
while (i <= 5) {
    printf("%d\n", i);      /* 1. no progress: i never changes */
}

while (i != 5) { i += 2; }  /* 2. steps past the target: 1, 3, 5 works, 2, 4, 6 does not */

unsigned int j = 5;
while (j >= 0) { j--; }     /* 3. an unsigned value is never negative */

while (x != 0.3) { ... }    /* 4. floating-point equality may never hold */

The third is the one to remember. Chapter 9 showed that 0u - 1 is the maximum value, so an unsigned condition of >= 0 is always true. Use int for a counter that may go below zero.

Reading input until it ends

The standard shape, and one you will use constantly:

#include <stdio.h>

int main(void)
{
    int c;
    int lines = 0, chars = 0;

    while ((c = getchar()) != EOF) {
        chars++;
        if (c == '\n') {
            lines++;
        }
    }
    printf("%d character(s) in %d line(s)\n", chars, lines);
    return 0;
}
one
two
three
14 character(s) in 3 line(s)

Three things: c is an int so that EOF is distinguishable (chapter 12), the brackets round the assignment are required (chapter 18), and the loop ends by itself when the input runs out, which is why a while and not a for is right here.

What this does NOT mean

A while loop does not always run. If the condition is false at the start, the body runs zero times.

The condition is not rechecked during the body. It is tested between passes. Changing the variable halfway through the body does not stop the loop early; break does.

munotes.in129

while Loops

The counter does not end at the last value used. It ends at the first value that failed the test.

while (n) is not the same as while (n > 0) for a signed n. while (n) is true for negative values too, which is what makes the reverse-digits loop fail to end on a negative input.

An infinite loop is not always a bug. while (1) with a break is a normal shape, and it is how a menu repeats.

Quick revision

  • while (condition) statement tests first, so it may run zero times.
  • A correct loop has initialisation, a condition that can fail, and progress towards failing it.
  • The counter ends at the first value that failed the test, not the last one used.
  • Reverse digits: d = n % 10; r = r * 10 + d; n = n / 10; until n is 0.
  • A negative input needs the sign taken off first, because % keeps the sign of the left operand.
  • Factorial: f = f * i with i from 1 to n, and 0! is 1. 20! is the largest that fits in 64 bits.
  • Unsigned arithmetic wraps, so while (j >= 0) on an unsigned j never ends.
  • while ((c = getchar()) != EOF) reads to the end of input; c must be an int.
  • while (1) with a break is a legitimate deliberate infinite loop.

Test yourself

1. How many times does this body run?

int i = 5;
while (i < 5) { i++; }

Zero. The condition is false before the first pass.

2. What is i after int i = 1; while (i <= 5) i++;?

  1. The loop stops when the test fails, which is the first time i is 6.

3. Trace n = 407 through the reverse-digits loop.

Pass 1: digit 7, reversed 7, n 40. Pass 2: digit 0, reversed 70, n 4. Pass 3: digit 4, reversed 704, n 0. Answer 704.

4. Why does the reverse-digits program handle the sign separately?

Because in C the remainder takes the sign of the left operand, so -5024 % 10 is -4, and while (n > 0) is false at once for a negative n. Taking the sign off first makes the loop work, and it is put back at the end.

5. What is wrong with unsigned int i = 10; while (i >= 0) i--;?

An unsigned value is never negative, so the condition is always true and the loop never ends. Once i reaches 0, decrementing wraps it to the maximum value.

6. Why must c be an int in while ((c = getchar()) != EOF)?

getchar must be able to return every character value and also EOF, which is a distinct negative value. A char has no spare value for it, so a char would either never compare equal to EOF or would compare equal to a real character.

munotes.in130

while Loops

7. What is the largest factorial that fits in an unsigned long long, and what happens beyond it?

20 factorial. Beyond that the value wraps modulo 2 to the 64, so the program prints a number that is not the factorial and reports no error.

What can be asked on this, and how to answer it

"Explain the while loop with syntax and an example." Give the syntax, say the condition is tested before each pass so the body may run zero times, and name the three parts a loop needs. Give a counting example and point out that the counter ends one past the last value used.

"Write a program using a while loop to reverse the digits of a number." Give this chapter's program and the trace table. Be ready for the three follow-ups: zero, a negative number, and a trailing zero.

"Write a program to find the factorial of a number." Give the while version with unsigned long long, the check for a negative input and the check for n > 20. Saying why 20 is the limit is what separates this from a copied answer.

"Distinguish between while and do-while." while tests before the body, so it may run zero times. do-while tests after, so it always runs at least once, and its while clause ends with a semicolon. Give input validation as the case for do-while.

"What is an infinite loop? Give two ways one happens by accident." A loop whose condition never becomes false. Accidentally: no progress in the body, and a condition that cannot fail, such as >= 0 on an unsigned variable. Add that while (1) with a break is a deliberate and useful form.

munotes.in131

The rest of this subject

These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.

Report or request
Done!