【问题标题】:How I can handle integer overflow?我如何处理整数溢出?
【发布时间】:2019-01-19 14:29:44
【问题描述】:

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

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

*为什么它适用于这个数字 338479759475637465765。但它不适用于 58678946895785。我在我的程序中使用的逻辑是当它超出范围时,int 变量给出一些 -1 或负值。看了很多文章,还是不太清楚。

【问题讨论】:

  • 忽略该行。我将一些代码从 isfloat() 编辑到 isInt()
  • perror() 的第二次和第三次使用没有意义。我
  • 另外请花点时间正确缩进代码。
  • num &gt; INT_MAX 永远不会是真的。由于numint,它的值永远不会大于INT_MAX。可能发生的情况是,当您输入一个大数字时,scanf 内部发生了溢出(这可能是 C 标准未定义的行为;我没有检查这一点),结果是放置了一个负值在int。然后这触发了你的测试num &lt; 0

标签: c int overflow


【解决方案1】:

strtol 将值转换为long int,其范围可能与int 不同。此外,如果值可以转换但在long int 的范围之外,则返回LONG_MAXLONG_MIN。在这种情况下,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 directoryMultihop attempted 的任何内容。

【讨论】:

  • 非常有帮助。但是如果我检查 long long int 的溢出,我不能这样做,对吧?您正在做的是将输入转换为 long in 以检查 int 的溢出,然后将其与 INT_MIN 或 INT_MAX 进行比较。明智地检查 long in 的溢出,我可以使用 strtoll 并将其与 LLONG_MIN 或 LLONG_MAX 进行比较。但是,如何我要检查 long long int 的溢出吗?
【解决方案2】:

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

如果转换的结果不能在对象中表示,则行为未定义。 C11dr §7.21.6.2 10

不要使用*scanf() family 来检测溢出。它可能在特定情况下有效,但不是一般情况。


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

替代方案:

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;
}

【讨论】:

  • 如果只有*scanf 函数会说“该值与strtoimax 的返回值相同,转换为适当的类型”。 ://
  • @AnttiHaapala 是的,“行为未定义”。是*scanf() 典型用法的杀手锏。
【解决方案3】:

你把你的类型弄混了。

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 的函数后立即使用它。

【讨论】:

  • 我明白了。但我仍然有一个疑问。在输入 long int 后,我​​必须与 INT_MAX 进行比较以检查 int 的溢出。然后当我需要 long int 输入时,我会检查 long long int。如果我输入 long long int ,那么我如何检查溢出。我的意思是,这里我想要 int 输入,所以我把它放在 long int 变量中并用 INT_MAX 检查它。但是,当我想取 long long int 时,如何检查溢出?
  • 我不同意。 sscanf 不是一个合理的建议,因为使用 sscanf无法判断值是否溢出。它仅适用于 iff 范围 long intint 不同。此外,OP 代码包含对换行符或 0 的检查,即该行在数字之后终止 - 你的将丢弃尾随字符。
  • 正确的做法是使用strtol 代码,检查ERANGE,然后与INT_{MAX|MIN} 进行比较
【解决方案4】:

如果一个必须使用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 &gt; 1e18的稀有平台上是不够的。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-25
    • 2013-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2012-11-02
    • 1970-01-01
    相关资源
    最近更新 更多