Skip to main content

Line Drawing - with data validation

Suppose we want to alter our program to ensure that the value entered by the user makes sense. For example, our current code will accept -3 as input and there is nothing to stop the user from doing this.

In this example we will alter the previous example to ensure that the user enters a number between 1 and 50 inclusive.

Original function:

This is the original code:

#include <stdio.h>
//declare what functions we will have:
int getNumStars();
void drawLine(int numStars);
int main(void)
{
int numStars = getNumStars();
drawLine(numStars);
return 0;
}
int getNumStars()
{
int numStars;
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);
return numStars;
}
void drawLine(int numStars)
{
int i;
for (i = 0; i < numStars; i++){
printf("*");
}
printf("\n");
}

Where do we make the change?

Since we are using functions, the code that does the user input is isolated to the getNumStars() function. Thus, this is the function we should change.

Flowchart

The general structure of the new getNumStars() function

Implementation:

We can follow the diagram to implement this process.

int getNumStars()
{
int numStars;
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);
//while the data is wrong:
while(numStars < 0 || numStars > 50){
//print error message
printf("Error: Number of stars must be between 0 and 50 inclusive.");
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);

}
return numStars;
}

Putting this all together

Putting all of this together we get the following program. Notice how the main() remains exactly as it was even though we had made significant changes to the way we handle the input. This is one of the reasons we use functions. It isolates changes and separates the details. It keeps our code clean and easy to follow.

#include <stdio.h>
//declare what functions we will have:
int getNumStars();
void drawLine(int numStars);
int main(void)
{
int numStars = getNumStars();
drawLine(numStars);
return 0;
}
int getNumStars()
{
int numStars;
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);
//while the data is wrong:
while(numStars < 0 || numStars > 50){
//print error message
printf("Error: Number of stars must be between 0 and 50 inclusive.");
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);

}
return numStars;
}
void drawLine(int numStars)
{
int i;
for (i = 0; i < numStars; i++){
printf("*");
}
printf("\n");
}

The do-while implementation

The last type of loop that we will look at is called the do-while loop. A do-while loop is similar to a while loop but it will always run at least one time.

The syntax for a do-while loop is this:

do{
//things you want to repeat in a loop
}while (<check to keep going with loop>);

This is very similar to while() syntax except the check is at the bottom of the loop. Thus, you must go through the loop at least one time before you check if the loop should continue.

This loop exists in C and many of the C-family languages but it is rarely used in practice. One of the reasons for this is because it is harder to see the condition for how the loop will end as it is buried at the bottom of the loop. Some newer languages such as python do not have do-while loop type constructs at all.

whiles are superior to do-whiles

While loops are generally PREFERRED over do-whiles. In general, the do-while loops can be written as while loops. This section is only being added for completeness.

One of the advantages of using a do while is that we do not have to repeat the prompt and read code as we will do one read before we decide if we wish to continue. If we do not want an error message, then a do-while loop is more succinct

	do{
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);

}while(numStars < 0 || numStars > 50);

However... an error message is really useful to tell the user why we were asking them to re-enter. Imagine your computer just telling you that you are wrong without actually telling you why. It would be a very frustrating experience.

However, we can't just stick the error message in. If we wrote the following the error message would show up even on the first iteration:

	do{
//print error message
printf("Error: Number of stars must be between 0 and 50 inclusive.");
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);

}while(numStars < 0 || numStars > 50);

There are two ways for us to deal with this issue. We can make the error message less of an error and more of an instruction...:

	do{
//print error message
printf("Number of stars must be between 0 and 50 inclusive.");
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);

}while(numStars < 0 || numStars > 50);

The above is not so explicitly an "error" and it does provide instruction to user. If this is good enough, then this is one solution. However, if you distinctly want to write something that clearly should not show up on the first run (such as the original), you will need to find a way to suppress that expression.

This can be done using a first-run flag and checking for that in our loop:

	//first run is truthy when it is the first run of the loop
int firstRun = 1;
do{
//when testing for something that is truthy or falsey, we should
//always test against 0. This is because 0 is the only false value
//while every non-zero is true. It is far safer to always test
//against 0.
if(firstRun != 0){
//print error message
printf("Error: Number of stars must be between 0 and 50 inclusive.");
//update firstRun variable
firstRun = 0;
}
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);

}while(numStars < 0 || numStars > 50);

Another possibility is to move the error to the bottom of the loop and doing a second check:

	do{
//prompt and read
printf("Please enter number of stars on the line: ");
scanf("%d", &numStars);

//if it is not correct, we need to print error
if(numStars < 0 || numStars > 50){
//print error message
printf("Number of stars must be between 0 and 50 inclusive.");
}
}while(numStars < 0 || numStars > 50);

Doing this of course means that there are two checks that are exactly the same within the loop.

As you can see, do-while comes with its own problems. It is best to simply avoid this loop in your own code.