【问题标题】:Validate max integer in scanf验证 scanf 中的最大整数
【发布时间】:2010-12-14 05:39:24
【问题描述】:

我想从标准输入读取一个 int,但我想验证用户是否超过了 int 最大值。我该怎么做?

int n;
scanf("%d", &n);

scanf 读取十进制输入并存储在 int 中,导致溢出。我该如何检查和避免这种情况?

【问题讨论】:

    标签: c scanf


    【解决方案1】:

    将其读入字符串并检查长度,然后在字符串上调用atol()sscanf() 将其转换为int。

    【讨论】:

    • 不是atoi,也不是sscanf。这些都不能防止溢出。由于显而易见的原因,“检查长度”不能用于防止溢出。
    • 您可以假设,如果输入的数字是 10 位数字,则它不适合 int。
    • 2000000000 的长度为 10 位,它适合 32 位 int 没有问题。
    • 如果您不明白为什么检查长度不起作用,请考虑INT_MAX == 2147483647 的环境,即 10 位数字。现在3000000000也是10位,但是超过了INT_MAX,导致溢出。
    【解决方案2】:

    另一种方法是设置要解析的最大位数。

    例如:

      int n;
      scanf("%5d", &n); // read at most 5 digits
      printf("%i\n", n);
    

    【讨论】:

      【解决方案3】:

      将数字的字符串表示形式转换为实际值并监视溢出的唯一方法是使用strto.. 组中的函数。在您的情况下,您需要读取数字的字符串表示形式,然后使用 strtol 函数对其进行转换。

      注意建议使用atoisscanf 执行最终转换的响应。这些函数都不能防止溢出。

      【讨论】:

      • 您能否展示一个完整的示例,说明如何转换为 long 并检查“溢出”错误?
      • 没什么可展示的。如果溢出strtolerrno 设置为ERANGE。只需检查errno 的值即可捕获溢出。
      • 那是 Ansi C 兼容 (C89) 吗?
      • 是的,现在是 strtol(以及 strto... 的其余函数)在 C89/90 中进行了描述。
      • 函数strtol 将检查范围是否适合long。但是,OP 正在询问int 的范围。因此,在对long 执行范围检查后,您必须另外将转换后的值与INT_MININT_MAX 进行比较,就像我在回答中所做的那样。
      【解决方案4】:

      想到了两种方法。

      如果您知道输入是类似整数的唯一输入,但第一个是有用的,但会“略微”超出整数范围:

      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 的大小。如果这是在longint 大小相同的平台上运行的,这是一种无用的方法。

      第二种方法是将输入读取为字符串并直接解析:

      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 转换来进行范围检查。

      【讨论】:

        【解决方案5】:

        使用strtol 将字符串转换为整数时,如果发生范围错误,errno 将被设置为ERANGE。这将告诉您用户输入的数字是否超出了long 的范围。如果要确定它是否超出int 的范围,则必须另外验证该数字是否大于INT_MIN 且小于INT_MAX。请注意,您必须#include &lt;limits.h&gt; 才能使用这些常量。

        因此,我建议使用以下代码从用户那里读取一个数字并对其进行验证:

        #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!
        

        【讨论】:

          猜你喜欢
          • 2013-05-01
          • 2021-07-22
          • 1970-01-01
          • 2019-04-23
          • 2017-08-12
          • 1970-01-01
          • 2021-01-23
          • 2021-12-25
          • 1970-01-01
          相关资源
          最近更新 更多