Проверить максимальное целое число в scanf

Я хочу прочитать int из стандартного ввода, но я хочу проверить, превышает ли пользователь максимальное значение int. Как мне это сделать?

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

scanf считывает десятичный ввод и сохраняет в int, вызывая переполнение. Как я могу проверить и избежать этого?


person António Silva    schedule 07.11.2009    source источник


Ответы (4)


Единственный способ преобразовать строковое представление числа в фактическое значение и следить за переполнением — это использовать функции из группы strto... В вашем случае вам нужно прочитать строковое представление числа, а затем преобразовать его с помощью функции strtol.

Остерегайтесь ответов, предлагающих использовать atoi или sscanf для окончательного преобразования. Ни одна из этих функций не защищает от переполнения.

person AnT    schedule 07.11.2009
comment
Можете ли вы показать полный пример того, как преобразовать в длинный и проверить ошибку переполнения? - person AlfaTeK; 03.12.2009
comment
Там не так много, чтобы показать. В случае переполнения strtol устанавливает errno в ERANGE. Просто проверьте значение errno, чтобы поймать переполнение. - person AnT; 03.12.2009
comment
И это совместимо с Ansi C (C89)? - person AlfaTeK; 03.12.2009
comment
Да, теперь это strtol (и остальные strto... функции) описаны в C89/90. - person AnT; 03.12.2009

Другой способ - установить максимальное количество цифр для анализа.

Например:

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

На ум приходят два метода.

Первый полезен, если вы знаете, что ввод является целочисленным, но страдает от «слегка» превышения целочисленного диапазона:

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 для проверки диапазона.

person wallyk    schedule 23.03.2014

Прочитайте его в строку и проверьте длину, затем вызовите atol() или sscanf() для строки, чтобы преобразовать ее в целое число.

person Martin Beckett    schedule 07.11.2009
comment
Не atoi и не sscanf. Ни один из них не защищает от переполнения. Проверка длины не может использоваться для предотвращения переполнения по очевидным причинам. - person AnT; 07.11.2009
comment
вы можете предположить, однако, что если введенное число состоит из 10 цифр, оно не уместится в int. - person Martin Beckett; 08.11.2009
comment
2000000000 имеет длину 10 цифр и без проблем помещается в 32-битное целое число. - person AnT; 08.11.2009
comment
На всякий случай, если вам не очевидно, почему проверка длины не работает, рассмотрите среду, где INT_MAX == 2147483647, что составляет 10 цифр. Теперь 3000000000 также состоит из 10 цифр, но превышает INT_MAX, что приводит к переполнению. - person syockit; 14.05.2018