Input validation in C is a pain in the ass, and requires a bit of work on your part. Do not rely on scanf to do the hard work for you, because it won't. If you enter an input like 12dw, scanf with the %d conversion specifier will successfully read, convert, and assign 12 to your target while leaving dw in the input stream to foul up the next read.
Ideally for interactive input, you should read your input into a text buffer and convert to the target type yourself. It will help protect against cases like the above, and it will help in case there's an obnoxiously long input that's attempting an overflow exploit or something like that.
So, start with a fixed-size array of char for your input buffer:
#define MAX_INPUT_LENGTH 13 // enough for 32-bit decimal integer, plus sign
char input[MAX_INPUT_LENGTH+1]; // +1 for string terminator
You'll use fgets to read the input:
if ( !fgets( input, sizeof input, stdin ) )
{
// error on input
}
else
{
// process input
}
The next check you'll want to make is to see if there's a newline in the input buffer - if not, then your user entered a value that's too large to represent as a native integer type. If that's the case, you'll want to discard the current input, then read and discard everything up to and including the next newline:
char *newline = strchr( input, '\n' );
if ( !newline )
{
while ( getchar() != '\n' ) // read and discard everything up to the next newline
;
}
else
{
// convert input
}
Now you're ready to convert your input from text to integer. Use the strtol library function for this (include stdlib.h for its prototype), as it will allow to check if there are any non-numeric, non-whitespace characters in the input.
char *chk;
height = (int) strtol( input, &chk, 0 );
if ( !isspace( *chk ) || *chk != 0 ) // include ctype.h for isspace
{
// conversion was unsuccessful, bad input
}
else
{
// check range
}
Putting all of that together, you get something like this:
#include <stdlib.h>
#include <ctype.h>
...
#define MAX_INPUT_LENGTH 13
...
int height;
int done = 0;
printf( "Welcome to Mario Pyramid Builder!\n" );
do
{
char input[MAX_INPUT_LENGTH+1];
height = -1; // initialize height to an invalid value
printf( "Enter a number from 0 to 23: " );
if ( !fgets( input, sizeof input, stdin ) )
{
fprintf( stderr, "Error on input, try again!\n" );
}
else
{
//
// make sure input wasn't too long for the input buffer by
// checking for a newline.
//
char *newline = strchr( input, '\n' );
if ( !*newline )
{
while ( getchar() != '\n' )
;
fprintf( stderr, "Input too long, try again!\n" );
}
else
{
//
// Convert text in input buffer to an integer. chk will point
// to the first character in input that is not a decimal digit.
// If that character is not 0 or whitespace, then we have
// bad (non-numeric) input.
//
char *chk;
height = strtol( input, &chk, 10 );
if ( !isspace( *chk ) || *chk != 0 )
{
fprintf( stderr, "Non-numeric input detected, try again!\n" );
}
else
{
//
// if height is between 0 and 23, done will be set to true,
// and we'll exit the loop.
//
done = height >= 0 && height <= 23;
}
}
}
} while ( !done );
argv?scanfreturns the number of successful conversions, which should be 1 in your case. If the return value is not 1, the user entered garbage, and you need to read all the characters up to the newline, and try again. Also, since the loop willbreakwhen the input is valid, you don't need thedo/while. A simplewhile(1)is all that's needed.