First of all, it is generally not recommended to use the function scanf_s
if you want your code to be portable to other platforms. It is usually better to use scanf
. If the only reason you are using scanf_s
instead is because the Microsoft compiler is telling you that you must do so, then I suggest that you add the line
#define _CRT_SECURE_NO_WARNINGS
to the very top of your source code file and the Microsoft compiler will accept that you use scanf
instead.
Also, the line
scanf_s("%d\n", &num);
is wrong. You should remove the \n
from the format string. See this question for more information:
What is the effect of trailing white space in a scanf() format string?
If you want your program to continue reprompting the user until the input is valid, then you can use the following code:
#include <stdio.h>
int main( void )
{
for (;;) //infinite loop, equivalent to while(1)
{
int num;
int days;
int week;
//get input from user and verify it
printf( "Enter number of days: " );
if (
scanf( "%d", &num ) != 1
||
num <= 0
)
{
printf( "Invalid input! Please try again.\n" );
continue;
}
//calculate number of weeks and days
week = num / 7;
days = num % 7;
//print result
printf( "%d days are %d weeks, %d days.\n", num, week, days );
//break out of infinite loop
break;
}
}
This program has the following behavior:
Enter number of days: 8
8 days are 1 weeks, 1 days.
Enter number of days: -3
Invalid input! Please try again.
Enter number of days: 0
Invalid input! Please try again.
Enter number of days: 14
14 days are 2 weeks, 0 days.
However, it is worth noting that this program will get stuck in an infinite loop if the user enters anything else except digits:
Enter number of days: abc
Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
Enter number of days: Invalid input! Please try again.
[...]
This is because scanf
will immediately fail without consuming the invalid input, so that the next call to scanf
will fail for exactly the same reason.
The function scanf
behaves this way, because it is not designed for user-based line input. Therefore, it would be better to not use this function. It would be better to use the function fgets
to always read a whole line of input as a string, and then to use the function strtol
to attempt to convert this input to an integer.
In this answer of mine to another question, I created a function get_int_from_user
which uses fgets
and strtol
instead of scanf
so that it does not have the problem mentioned above. This function will perform extensive input validation and automatically reprompt the user if the input is not a valid integer or cannot be converted to an int
for some other reason.
If I rewrite your program to use this function, it will look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
//declare function prototype
int get_int_from_user( const char *prompt );
int main( void )
{
for (;;) //infinite loop, equivalent to while(1)
{
int num;
int days;
int week;
//read integer from user
num = get_int_from_user( "Enter number of days: " );
//verify that number is positive
if ( num <= 0 )
{
printf( "Invalid input! Please try again.\n" );
continue;
}
//calculate number of weeks and days
week = num / 7;
days = num % 7;
//print result
printf( "%d days are %d weeks, %d days.\n", num, week, days );
//break out of infinite loop
break;
}
}
int get_int_from_user( const char *prompt )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
long l;
//prompt user for input
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable input error!\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "Line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Unrecoverable error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "Number out of range error!\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6sdfj23jlj" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto continue_outer_loop;
}
}
return l;
continue_outer_loop:
continue;
}
}
As you can see, the program no longer gets stuck in an infinite loop if I enter something else than digits:
Enter number of days: abc
Error converting string to number!
Enter number of days: 8abc
Unexpected input encountered!
Enter number of days: 8
8 days are 1 weeks, 1 days.