'do...while' vs. 'while'

Viewed 135353

Possible Duplicates:
While vs. Do While
When should I use do-while instead of while loops?

I've been programming for a while now (2 years work + 4.5 years degree + 1 year pre-college), and I've never used a do-while loop short of being forced to in the Introduction to Programming course. I have a growing feeling that I'm doing programming wrong if I never run into something so fundamental.

Could it be that I just haven't run into the correct circumstances?

What are some examples where it would be necessary to use a do-while instead of a while?

(My schooling was almost all in C/C++ and my work is in C#, so if there is another language where it absolutely makes sense because do-whiles work differently, then these questions don't really apply.)

To clarify...I know the difference between a while and a do-while. While checks the exit condition and then performs tasks. do-while performs tasks and then checks exit condition.

31 Answers

I like to understand these two as:
while -> 'repeat until',
do ... while -> 'repeat if'.

Even though there are plenty of answers here is my take. It all comes down to optimalization. I'll show two examples where one is faster then the other.

Case 1: while

string fileName = string.Empty, fullPath = string.Empty;

while (string.IsNullOrEmpty(fileName) || File.Exists(fullPath))
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}

Case 2: do while

string fileName = string.Empty, fullPath = string.Empty;

do
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}
while (File.Exists(fullPath));

So there two will do the exact same things. But there is one fundamental difference and that is that the while requires an extra statement to enter the while. Which is ugly because let's say every possible scenario of the Guid class has already been taken except for one variant. This means I'll have to loop around 5,316,911,983,139,663,491,615,228,241,121,400,000 times. Every time I get to the end of my while statement I will need to do the string.IsNullOrEmpty(fileName) check. So this would take up a little bit, a tiny fraction of CPU work. But do this very small task times the possible combinations the Guid class has and we are talking about hours, days, months or extra time?

Of course this is an extreme example because you probably wouldn't see this in production. But if we would think about the YouTube algorithm, it is very well possible that they would encounter the generation of an ID where some ID's have already been taken. So it comes down to big projects and optimalization.

Even in educational references you barely would find a do...while example. Only recently, after reading Ethan Brown beautiful book, Learning JavaScript I encountered one do...while well defined example. That's been said, I believe it is OK if you don't find application for this structure in you routine job.

It's true that do/while loops are pretty rare. I think this is because a great many loops are of the form

while(something needs doing)
    do it;

In general, this is an excellent pattern, and it has the usually-desirable property that if nothing needs doing, the loop runs zero times.

But once in a while, there's some fine reason why you definitely want to make at least one trip through the loop, no matter what. My favorite example is: converting an integer to its decimal representation as a string, that is, implementing printf("%d"), or the semistandard itoa() function.

To illustrate, here is a reasonably straightforward implementation of itoa(). It's not quite the "traditional" formulation; I'll explain it in more detail below if anyone's curious. But the key point is that it embodies the canonical algorithm, repeatedly dividing by 10 to pick off digits from the right, and it's written using an ordinary while loop... and this means it has a bug.

#include <stddef.h>

char *itoa(unsigned int n, char buf[], int bufsize)
{
    if(bufsize < 2) return NULL;
    char *p = &buf[bufsize];
    *--p = '\0';

    while(n > 0) {
        if(p == buf) return NULL;
        *--p = n % 10 + '0';
        n /= 10;
    }

    return p;
}

If you didn't spot it, the bug is that this code returns nothing — an empty string — if you ask it to convert the integer 0. So this is an example of a case where, when there's "nothing" to do, we don't want the code to do nothing — we always want it to produce at least one digit. So we always want it to make at least one trip through the loop. So a do/while loop is just the ticket:

    do {
        if(p == buf) return NULL;
        *--p = n % 10 + '0';
        n /= 10;
    } while(n > 0);

So now we have a loop that usually stops when n reaches 0, but if n is initially 0 — if you pass in a 0 — it returns the string "0", as desired.

As promised, here's a bit more information about the itoa function in this example. You pass it arguments which are: an int to convert (actually, an unsigned int, so that we don't have to worry about negative numbers); a buffer to render into; and the size of that buffer. It returns a char * pointing into your buffer, pointing at the beginning of the rendered string. (Or it returns NULL if it discovers that the buffer you gave it wasn't big enough.) The "nontraditional" aspect of this implementation is that it fills in the array from right to left, meaning that it doesn't have to reverse the string at the end — and also meaning that the pointer it returns to you is usually not to the beginning of the buffer. So you have to use the pointer it returns to you as the string to use; you can't call it and then assume that the buffer you handed it is the string you can use.

Finally, for completeness, here is a little test program to test this version of itoa with.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int n;
    if(argc > 1)
        n = atoi(argv[1]);
    else {
        printf("enter a number: "); fflush(stdout);
        if(scanf("%d", &n) != 1) return EXIT_FAILURE;
    }

    if(n < 0) {
        fprintf(stderr, "sorry, can't do negative numbers yet\n");
        return EXIT_FAILURE;
    }

    char buf[20];
    printf("converted: %s\n", itoa(n, buf, sizeof(buf)));

    return EXIT_SUCCESS;
}
Related