我如何处理整数溢出?

时间:2019-01-19 14:29:45

标签: c int overflow

我正在尝试处理整数溢出。我的代码是:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<limits.h>

int isInt (char *s)
{
 char *ep = NULL;
 long i = strtol (s, &ep, 10);
 if ((*ep == 0) || (!strcmp(ep,"\n")))
    return 1;  // it's an int

 return 0;  
} 

int main()
{
char *buffer = NULL;
size_t count = 0;
ssize_t ret;
//AMINO *a_acid;
int num;

for(;;)
{   
printf("Please enter an integer:");
if((ret = getline(&buffer, &count, stdin)) < 0)
{
    perror("getline: error\n");
    free(buffer);
    exit(EXIT_FAILURE);
}

if(!isInt(buffer))
{
    perror("you are not entering int , Try again:");
    continue;
}
sscanf(buffer, "%d",&num);
printf("%d\n", num);
if ((num > INT_MAX)|| (num < 0))
{
    perror("you overflowed int variable , Try again:\n ");
    continue;
}
break;
}

}

现在,我正在检查此代码的响应方式。我看到了一些奇怪的东西,当我输入这么大的数字时,它就会被发现。但是有时没有被发现。 这是我的终端视图:

> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ gcc torson.c 
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ ./a.out
> Please enter an integer:ksdjfjklh 
> you are not entering int , Try again:: Success
> Please enter an integer:338479759475637465765
> -1 
> you overflowed int variable , Try again:  : Numerical result out of  
> range 
> Please enter an integer:58678946895785 
> 1103697833
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$

*为什么它对这个数字338479759475637637465765起作用。但是对于58678946895785785却不起作用。我在程序中使用的逻辑是当它越界时,则int变量给出一些-1或负值。我读了很多文章,仍然不太清楚。

4 个答案:

答案 0 :(得分:2)

sscanf(buffer, any_format_without_width, &anytype);不足以检测溢出。

  

如果无法在对象中表示转换结果,则行为未定义。 C11dr§7.21.6.210

请勿使用*scanf()系列来检测溢出。在某些情况下可能会起作用,但通常不会。


请改为使用strto**()函数。然而,甚至OP的isInt()也被错误地编码,因为它错误地将isInt("\n")isInt("")isInt("999..various large values ...999")评估为好int

替代:

bool isint_alt(const char *s) {
  char *endptr;
  errno = 0;
  long y = strtol(s, &endptr, 10);

  if (s == endptr) {
    return false; // No conversion
  }

  if (errno == ERANGE) {
    return false; // Outside long range
  }

  if (y < INT_MIN || y > INT_MAX) {
    return false; // Outside int range
  }

  // Ignore trailing white space
  while (isspace((unsigned char)*endptr)) {
    endptr++;
  }

  if (*endptr) {
    return false; // Trailing junk
  }

  return true;
}

答案 1 :(得分:2)

strtol将值转换为long int,其范围可能与int不同。此外,如果该值可以转换,但在LONG_MAX的范围内 之外,则返回LONG_MINlong int。在这种情况下,errno将被设置为ERANGE(但不是!)。此外,在匹配失败的情况下,返回的值为0,但未设置errno;但ep指向字符串的开头。

int isInt (char *s)
{
   char *ep = NULL;

   // zero errno first!
   errno = 0;
   long i = strtol (s, &ep, 10);
   if (errno) {
       return 0;
   }

   // matching failure.
   if (ep == s) {
       return 0;
   }

   // garbage follows
   if (! ((*ep == 0) || (!strcmp(ep,"\n")))) {
      return 0;
   }

   // it is outside the range of `int`
   if (i < INT_MIN || i > INT_MAX) {
      return 0;
   }

   return 1; 
} 

dbush关于perror的使用的说法是正确的。 strtol仅在long溢出的情况下设置错误,这不是函数中唯一可能的失败情况,因此perror可以打印类似Is a directory或{{1 }}。

答案 2 :(得分:1)

您正在混淆类型。

isInt函数中,您使用strtol,该函数将返回long来检查值。然后,在main函数中,将sscanf%d一起使用,这会读入int

在您的系统上,看来long是64位,而int是32位。因此strtol无法完全转换338479759475637465765,因为它大于64位变量可以容纳的大小。然后,您尝试转换58678946895785,它适合64位变量,而适合32位变量。

您应该将sscanf读入long。然后,您可以将值与INT_MAX进行比较:

long num;
...
sscanf(buffer, "%ld", &num);
printf("%ld\n", num);
if ((num > INT_MAX)|| (num < INT_MIN))
{
    printf("you overflowed int variable , Try again:\n ");
    continue;
}

还请注意,在此处调用perror没有任何意义。您只有在调用设置了errno的函数之后才使用它。

答案 3 :(得分:1)

如果一个必须使用sscanf()来检测int溢出而不是可靠的strtol(),则有一个麻烦的方法。

使用较宽的类型和宽度限制以防止扫描时溢出。

bool isint_via_sscanf(const char *s) {
  long long y;
  int n = 0;
  if (sscanf(s, "18%lld %n", &y, &n) != 1) {  // Overflow not possible
    return false;  // Conversion failed 
  }

  if (y < INT_MIN || y > INT_MAX) {
    return false; // Outside int range
  }

  if (s[n]) {
    return false;  // Trailing junk
  }

  return true;  
}

INT_MAX > 1e18的稀有平台上是不够的。

它还会错误地将类似"lots of leading space and/or lot of leading zeros 000123"的输入返回为无效。

使用sscanf()编写更复杂的代码可以解决这些缺点,而最好的方法是strto*()