Обнаружение переполнения для целого числа в C - PullRequest
0 голосов
/ 26 августа 2018
#include <stdio.h>

int reverse(int);
int reverse(int x) {
  int negative = 0;
  if (x < 0)
    negative = x;
  if (negative != 0) {
    x = 0 - x;
  }
  int y = 0, temp;
  while (x > 0) {
    temp = x % 10;
    x = x / 10;
    y = (y * 10) + temp;
  }
  if (negative == 0) {
    return y;
  } else {
    return 0 - y;
  }
}
int main() {
  int x, y;
  printf("Enter your number: \n");
  scanf("%d", &x);
  y = reverse(x);
  printf("The reversed number is: %d\n", y);
  return 0;
}

Эта программа восстанавливает целое число со знаком. Я не смог проверить, находится ли уважаемое целое число y вне границ. Я не могу достичь ясности по теме переполнения. Если я попрошу компилятор, использующий scanf, сканировать целое число и ввести целое число, выходящее за пределы целого числа, что произойдет? Изменяется ли значение при сохранении?

Ответы [ 2 ]

0 голосов
/ 26 августа 2018

В стандарте C переполнение является исключительным условием , и поведение тогда не определено для стандарта. Из черновика №1570 для С11, 6.5 Выражения § 5:

Если во время вычисления выражения возникает исключительное условие (то есть, если результат не определен математически или не находится в диапазоне представимых значений для его тип), поведение не определено.

Это означает, что вы не можете обработать переполнение способом, который будет определен для стандарта. При этом большинство компиляторов просто сохраняют младшие биты результата, которые могут поместиться в представлении типа, и игнорируют биты высшего порядка. Это то, что делают MSVC, gcc и clang.

С этого момента я буду предполагать, что ваша система использует int32_t (соответственно int16_t или int64_t) для подписанного int и игнорирует переполнение. Он не обязателен для каждого стандарта, но достаточно распространен и, вероятно, это то, что делает ваша система.

В вашем коде есть 2 возможных переполнения. Во-первых, это функция scanf: как говорит @ n.m., Ничего не сказано о поведении этого семейства функций, если входная последовательность не соответствует типу. Я предполагаю, что представленный номер является приемлемым в виде целого числа со знаком (между INT_MIN и INT_MAX). В любом случае, пользователь может обнаружить его просто: просто отобразите x: если это не просто набранный номер, то произошло переполнение.

Второе здесь:

y = (y * 10) + temp;

Здесь снова легко проверить переполнение, выполнив обратные операции и убедившись, что все в порядке:

int next = (y * 10) + temp;
if ((next < 0) || (y != (next - temp) / 10)) {
    // an overflow occured...
    ...

Теоретически, ничто не может гарантировать, что отрицание положительного int является действительным отрицательным int, но это верно для типов intXX_t, поэтому этого должно быть достаточно для обнаружения переполнения в функции reverse.

0 голосов
/ 26 августа 2018

Похоже, у вас есть два разных вопроса относительно переполнения.

  1. Как обнаружить переполнение в целочисленной арифметике?
  2. Как обнаружить переполнение в функциях преобразования, таких как scanf?

Ответы следующие.

  1. Нет универсального способа, вы должны делать это в каждом конкретном случае.Например, если y <= INT_MAX / 10, вы можете быть уверены, что y * 10 не переполнится.То же самое относится к ... + temp.
  2. scanf, и у друзей нет никакого способа защитить вас от переполнения, кроме ограничения ширины поля и, следовательно, диапазона ввода (но вы не можете ограничить диапазон точно до INT_MAXесли вы читаете десятичную).Если отсканированное значение не соответствует типу назначения, поведение не определено.Единственный способ безопасно конвертировать строку с точным диапазоном - с strtol и друзьями.Эти функции обнаруживают переполнение и устанавливают errno соответственно.
...