Как я могу справиться с целочисленным переполнением? - PullRequest
0 голосов
/ 19 января 2019

Я пытаюсь обработать целочисленное переполнение. Мой код:

#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 или отрицательное значение. Я прочитал много статей, до сих пор это не совсем понятно.

Ответы [ 4 ]

0 голосов
/ 19 января 2019

Если один должен использовать 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*().

0 голосов
/ 19 января 2019

sscanf(buffer, any_format_without_width, &anytype); является недостаточным , достаточным для обнаружения переполнения.

если результат преобразования не может быть представлен в объекте, поведение не определено. C11dr §7.21.6.2 10

Не используйте семейство *scanf() для обнаружения переполнения. Может работать в некоторых случаях, но не в целом.


Вместо этого используйте strto**() функции. Тем не менее, даже «OP» 1015 * неправильно закодированы, поскольку неправильно оценивают 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;
}
0 голосов
/ 19 января 2019

strtol преобразует значение в long int, диапазон которого может отличаться от int. Кроме того, он возвращает LONG_MAX или LONG_MIN, если значение может быть преобразовано, но составляет вне диапазона для long 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 или Multihop attempted.

0 голосов
/ 19 января 2019

Вы перепутали свои типы.

В функции 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.

...