【发布时间】:2010-12-14 05:39:24
【问题描述】:
我想从标准输入读取一个 int,但我想验证用户是否超过了 int 最大值。我该怎么做?
int n;
scanf("%d", &n);
scanf 读取十进制输入并存储在 int 中,导致溢出。我该如何检查和避免这种情况?
【问题讨论】:
我想从标准输入读取一个 int,但我想验证用户是否超过了 int 最大值。我该怎么做?
int n;
scanf("%d", &n);
scanf 读取十进制输入并存储在 int 中,导致溢出。我该如何检查和避免这种情况?
【问题讨论】:
将其读入字符串并检查长度,然后在字符串上调用atol() 或sscanf() 将其转换为int。
【讨论】:
atoi,也不是sscanf。这些都不能防止溢出。由于显而易见的原因,“检查长度”不能用于防止溢出。
2000000000 的长度为 10 位,它适合 32 位 int 没有问题。
INT_MAX == 2147483647 的环境,即 10 位数字。现在3000000000也是10位,但是超过了INT_MAX,导致溢出。
另一种方法是设置要解析的最大位数。
例如:
int n;
scanf("%5d", &n); // read at most 5 digits
printf("%i\n", n);
【讨论】:
将数字的字符串表示形式转换为实际值并监视溢出的唯一方法是使用strto.. 组中的函数。在您的情况下,您需要读取数字的字符串表示形式,然后使用 strtol 函数对其进行转换。
注意建议使用atoi 或sscanf 执行最终转换的响应。这些函数都不能防止溢出。
【讨论】:
strtol 将errno 设置为ERANGE。只需检查errno 的值即可捕获溢出。
strtol(以及 strto... 的其余函数)在 C89/90 中进行了描述。
strtol 将检查范围是否适合long。但是,OP 正在询问int 的范围。因此,在对long 执行范围检查后,您必须另外将转换后的值与INT_MIN 和INT_MAX 进行比较,就像我在回答中所做的那样。
想到了两种方法。
如果您知道输入是类似整数的唯一输入,但第一个是有用的,但会“略微”超出整数范围:
long x;
if (1 != scanf("%ld", &x))
error "not a number or other i/o error"
else
if (x < -MAXINT || x > MAXINT)
error "exceeds integer range"
else printf "it is an integer";
这对编译器和平台有很大的依赖性。请注意,C 语言只保证long 大于或等于int 的大小。如果这是在long 和int 大小相同的平台上运行的,这是一种无用的方法。
第二种方法是将输入读取为字符串并直接解析:
char buf [1000];
if (1 != scanf ("%1000s", buf))
error "i/o error";
size_t len = strspn (buf, "0123456789+-");
if (len != strlen (buf))
error "contains invalid characters";
long number = 0;
int sign = 1;
for (char *bp = buf; *bp; ++bp)
{
if (*bp == '-')
{
sign = -sign;
continue;
}
if (*bp == '+')
continue;
number = number * 10 + *bp - '0';
if (number > MAXINT)
error "number too large";
}
if (sign < 0)
number = -number;
这段代码有几个缺点:它依赖于long 大于int;它允许加号和减号出现在字符串的任何位置。它可以很容易地扩展以允许除以十为基数之外的数字。
可能的第三种方法可能是输入一个字符串检查字符集并使用double 转换来进行范围检查。
【讨论】:
使用strtol 将字符串转换为整数时,如果发生范围错误,errno 将被设置为ERANGE。这将告诉您用户输入的数字是否超出了long 的范围。如果要确定它是否超出int 的范围,则必须另外验证该数字是否大于INT_MIN 且小于INT_MAX。请注意,您必须#include <limits.h> 才能使用这些常量。
因此,我建议使用以下代码从用户那里读取一个数字并对其进行验证:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
int main( void )
{
char buffer[1024], *p;
long num_long;
int num_int;
//prompt user for input
printf( "Enter a number: " );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable error reading from input!\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 )
{
printf( "Line input was too long!\n" );
exit( EXIT_FAILURE );
}
//attempt to convert string to number
errno = 0;
num_long = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number\n" );
exit( EXIT_FAILURE );
}
//make sure that no range error occurred
if ( errno == ERANGE || num_long < INT_MIN || num_long > INT_MAX )
{
printf( "Range error!\n" );
exit( EXIT_FAILURE );
}
//range is ok, so we can convert to int
num_int = (int)num_long;
//make sure that remainder of line contains only whitespace,
//so that input such as "6sdfh4q" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
exit( EXIT_FAILURE );
}
}
//number was successfully converted, so we print it
printf( "You entered the following valid number: %d\n", num_int );
}
输入有效数字时该程序的示例输出:
Enter a number: 65
You entered the following valid number: 65
输入无效输入时该程序的示例输出:
Enter a number: 6sdfh4q
Unexpected input encountered!
输入超出范围输入时该程序的示例输出:
Enter a number: 3000000000
Range error!
【讨论】: