В нескольких современных языках программирования (включая 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-разрядного целого числа со знаком). Переменная суммы будет иметь значение -294967296 (а не значение 4000000000); скорее всего, это не то, что предполагал (гипотетический) разработчик метода sumList.
Очевидно, что существуют различные методы, которые могут использоваться разработчиками, чтобы избежать возможности целочисленного переполнения, например, использование такого типа, как Java _ 5_ или _ 6_ ключевое слово и переключатель компилятора /checked
в C #.
Однако меня интересует вопрос, почему эти языки были по умолчанию предназначены для того, чтобы в первую очередь разрешать целочисленные переполнения, вместо, например, создания исключения, когда операция выполняется во время выполнения, что приведет к переполнение. Похоже, такое поведение поможет избежать ошибок в тех случаях, когда разработчик не учитывает возможность переполнения при написании кода, который выполняет арифметическую операцию, которая может привести к переполнению. (Эти языки могли включать что-то вроде ключевого слова unchecked, которое могло бы обозначать блок, в котором разрешено целочисленное переполнение без возникновения исключения, в тех случаях, когда такое поведение явно предусмотрено разработчиком; C # на самом деле есть.)
Сводится ли ответ просто к производительности - разработчики языков не хотели, чтобы в их соответствующих языках по умолчанию использовались «медленные» арифметические целочисленные операции, когда среде выполнения нужно было бы проделать дополнительную работу, чтобы проверить, произошло ли переполнение, для каждой применимой арифметики операция - и это соображение производительности перевешивает ценность предотвращения «тихих» сбоев в случае непреднамеренного переполнения?
Есть ли другие причины для этого решения о языковом дизайне, кроме соображений производительности?