首先,如果您希望您的代码可以移植到其他平台,一般不建议使用函数scanf_s。通常最好使用scanf。如果您使用 scanf_s 的唯一原因是因为 Microsoft 编译器告诉您必须这样做,那么我建议您添加该行
#define _CRT_SECURE_NO_WARNINGS
到源代码文件的最顶部,Microsoft 编译器将接受您改用scanf。
另外,线
scanf_s("%d\n", &num);
是错的。您应该从格式字符串中删除\n。有关更多信息,请参阅此问题:
What is the effect of trailing white space in a scanf() format string?
如果您希望您的程序继续重新提示用户,直到输入有效,那么您可以使用以下代码:
#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;
}
}
该程序具有以下行为:
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.
但是,值得注意的是,如果用户输入除数字以外的任何其他内容,该程序将陷入无限循环:
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.
[...]
这是因为scanf 将立即失败而不会消耗无效输入,因此对scanf 的下一次调用将由于完全相同的原因而失败。
函数scanf 的行为是这样的,因为它不是为基于用户的行输入而设计的。因此,最好不要使用此功能。最好使用函数fgets 始终将整行输入读取为字符串,然后使用函数strtol 尝试将此输入转换为整数。
在this answer of mine to another question 中,我创建了一个函数get_int_from_user,它使用fgets 和strtol 而不是scanf,这样就不会出现上述问题。如果输入不是有效整数或由于其他原因无法转换为int,此函数将执行广泛的输入验证并自动重新提示用户。
如果我重写您的程序以使用此功能,它将如下所示:
#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;
}
}
如您所见,如果我输入数字以外的其他内容,程序将不再陷入无限循环:
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.