В некоторых современных языках программирования (включая C ++, Java и C #) этот язык допускает целочисленное переполнение во время выполнения без возникновения каких-либо условий ошибки.
Например, рассмотрим этот (надуманный) метод C #, который не учитывает возможность переполнения / недостаточного заполнения. (Для краткости метод также не обрабатывает случай, когда указанный список является пустой ссылкой.)
//Returns the sum of the values in the specified list.
private static int sumList(List<int> list)
{
int sum = 0;
foreach (int listItem in list)
{
sum += listItem;
}
return sum;
}
Если этот метод вызывается следующим образом:
List<int> list = new List<int>();
list.Add(2000000000);
list.Add(2000000000);
int sum = sumList(list);
В методе sumList()
произойдет переполнение (поскольку тип int
в C # представляет собой 32-разрядное целое число со знаком, а сумма значений в списке превышает значение максимального 32-разрядного целого числа со знаком ). Переменная sum будет иметь значение -294967296 (не значение 4000000000); скорее всего, это не то, что (гипотетический) разработчик метода sumList намеревался.
Очевидно, что существуют разные методы, которые могут использоваться разработчиками, чтобы избежать возможности целочисленного переполнения, такие как использование типа, подобного Java BigInteger
или checked
ключевое слово и /checked
переключатель компилятора в C #.
Однако вопрос, который меня интересует, заключается в том, почему эти языки были спроектированы так, чтобы по умолчанию разрешать целочисленные переполнения в первую очередь, а не, например, вызывать исключение при выполнении операции во время выполнения, которая бы привести к переполнению. Кажется, что такое поведение поможет избежать ошибок в случаях, когда разработчик не учитывает возможность переполнения при написании кода, который выполняет арифметическую операцию, которая может привести к переполнению. (Эти языки могли включать что-то вроде «непроверенного» ключевого слова, которое могло бы обозначить блок, в котором допускается целочисленное переполнение без возникновения исключения, в тех случаях, когда такое поведение явно задано разработчиком; C # фактически делает есть это .)
Ответ сводится просто к производительности - разработчики языков не хотели, чтобы на их соответствующих языках по умолчанию использовались «медленные» арифметические целочисленные операции, когда среде выполнения нужно было бы выполнить дополнительную работу, чтобы проверить, произошло ли переполнение, при каждая применимая арифметическая операция - и это соображение производительности перевешивало значение избежания «тихих» отказов в случае непреднамеренного переполнения?
Существуют ли и другие причины для этого решения по проектированию языка, кроме соображений производительности?