Introduction
In the previous tutorial, we learned how to make our code more concise and flexible by using for loops. In this tutorial, we will learn about another kind of loop: the while loop. We will use this loop to make our code more robust by screening out bad user input.
Code
In our guess my secret number game from the previous tutorials, what we really want is the user to input a number between 1 and 10. Anything outside of that range, should be rejected as an invalid guess. So, here’s how to validate the user’s input:
int guess = 0;
while (guess < 1 || guess > 10)
{
cout << “Guess a number between 1 and 10? ” << endl;
cin >> guess;
}
First, this code declares a new variable called guess. Then the while loop checks to see whether the loop should be executed. This is like the second (or middle) part of the for loop from last time. The double vertical line (||) means or, meaning that if the left hand side or the right hand side are true, then the whole expressions should be true. The left side (guess < 1) is true, since zero is less than one, while the right hand side (guess > 10) is false, so the whole expression is true and the loop is executed. The user is then prompted to enter a guess and after they enter a guess, the loop check is evaluated again. If the user inputs a number between 1 and 10, the loop stops. If not, the loop keeps going until the user inputs a number between 1 and 10.
Validating user input is extremely important! Probably the largest source of bugs in software comes from a naïve assumption that the user will know what they should input and that they won’t deviate from that (intentionally or not). If you have ever heard of SQL injection hacks, they are basically caused because the programmer didn’t properly validate what the user typed in.
You might be thinking, wait, can’t a for loop do that as well? The answer is yes. For loops and while loops can always be converted from one to the other. Here’s what the previous code would look like as a for loop:
for (int guess = 0; guess < 1 || guess > 10; ; )
{
cout << “Guess a number between 1 and 10?” << endl;
cin >> guess;
}
The only strange part of this loop is the third parameter in the for loop. This is the code that is executed after each loop executed and before the loop continuation check is done. (This is typically where the variable declared and set to a value in the first part of the loop is incremented.) Since we don’t need to modify the guess variable, we just leave it blank.
So, how do you know which loop to use? Whenever you are looping through a list of numbers use a for loop. For example, if you wanted to loop through all of the numbers from 1 to 10 or if you wanted to loop through all of the even numbers from 50 to 100. Whenever you want to loop until something happens, use a while loop. For example, to keep looping until the user types in valid input.
There is another loop called the do while loop. This loop is very similar to the while loop, but it doesn’t do the loop continuation check before the loop executes, only after each loop. So, here is what it would look like:
int guess = 0;
do
{
cout << “Guess a number between 1 and 10?” << endl;
cin >> guess;
} while (guess < 1 || guess > 10)
So, unlike the while loop, which does a check before the loop starts, the do while loop executes the loop once and then checks to see if the input is valid.
The do while loop is very uncommon. I think the reason that it is uncommon is that it can be easily replaced by a while loop and the while loop is slightly more flexible. It’s more flexible because the while loop doesn’t have to be executed, whereas the do while has to be executed at least once. So, in places where a do while might be used, usually a while loop is used along with data that is set to have the loop executed at least once, like the example above. The guess variable is set to an invalid guess (zero), so that the loop is executed. This is the preferred way to do things, so that in the future if we change the program in a way that we don’t have to prompt the user for input, then no modifications are required. One example would be if we changed the program to take guesses as an optional command line argument. In this case, we wouldn’t want to prompt the user since they already told us their guess.
Handling the user typing in random letters is more difficult to handle. In order to do that, we need change the loop to look something like this:
int guess = 0;
while (guess < 1 || guess > 10)
{
cout << "Guess a number between 1 and 10?" << endl;
cin >> guess;
if (cin.gcount() != 1)
{
cin.clear();
cin.ignore(numeric_limits<int>::max(), "\n");
}
}
If the user enters in something other than a number, then gcount will not be equal to one. When this happens, then we want to clear the errors from cin and ignore all characters in the input stream. This will make it so that letters are ignored and the user will be forced to type in a number.
Attached below is the final version of the guess my number game, this time with a while loop to validate the user’s input.
Summary
In this tutorial, we learned how to make our code more robust by using a while loop to verify that the guess that the user typed in was valid.
In the next tutorial, we will be going over functions. Functions are a great way to reuse code and to abstract away solutions to common problems.
If you have any questions or comments about what was covered here, post them to the comments. I watch them closely and will respond and try to help you out.
Top Comments