Вопрос по – Почему языки не вызывают ошибки при целочисленном переполнении по умолчанию?
В нескольких современных языках программирования (включая C ++, Java и C #) язык позволяетцелочисленное переполнение происходить во время выполнения без возникновения каких-либо условий ошибки.
Например, рассмотрим этот (надуманный) метод C #, который не учитывает возможность переполнения / недостаточного заполнения. (Для краткости способ также не обрабатывает случай, когда указанный список является нулевой ссылкой.)
<code>//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; } </code>
Если этот метод вызывается следующим образом:
<code>List<int> list = new List<int>(); list.Add(2000000000); list.Add(2000000000); int sum = sumList(list); </code>
Переполнение произойдет вsumList()
метод (потому чтоint
в C # введите 32-разрядное целое число со знаком, а сумма значений в списке превышает значение максимального 32-разрядного целого числа со знаком). Переменная sum будет иметь значение -294967296 (не значение 4000000000); скорее всего, это не то, что (гипотетический) разработчик метода sumList намеревался.
Очевидно, что существуют разные методы, которые могут использоваться разработчиками, чтобы избежать возможности целочисленного переполнения, такие как использование типа, такого как Java.BigInteger
, илиchecked
ключевое слово и/checked
переключатель компилятора в C #.
Однако вопрос, который меня интересует, заключается в том, почему эти языки были спроектированы так, чтобы по умолчанию разрешать целочисленные переполнения, а не, например, вызывать исключение, когда во время выполнения выполняется операция, которая может привести к переполнение. Кажется, что такое поведение поможет избежать ошибок в случаях, когда разработчик не учитывает возможность переполнения при написании кода, который выполняет арифметическую операцию, которая может привести к переполнению. (Эти языки могли включать что-то вроде ключевого слова «unchecked», которое может обозначать блок, в котором допускается целочисленное переполнение без возникновения исключения, в тех случаях, когда такое поведение явно задано разработчиком; на самом деле C #есть ли это.)
Ответ просто сводится к производительности - разработчики языка не хотели, чтобы их соответствующие языки по умолчанию имели "медленный" целочисленные арифметические операции, где среде выполнения необходимо будет выполнить дополнительную работу, чтобы проверить, не произошло ли переполнение для каждой применимой арифметической операции - и это соображение производительности перевешивало значение избегания «тихого» вывода; сбои в случае непреднамеренного переполнения?
Существуют ли другие причины для этого решения о дизайне языка, кроме соображений производительности?
что производительность - довольно веская причина. Если вы рассматриваете каждую инструкцию в типичной программе, которая увеличивает целое число, и если вместо простого оператора добавить 1, то нужно было каждый раз проверять, будет ли добавление 1 переполнять тип, тогда затраты в дополнительных циклах будут довольно серьезными.
ение на 0 - неопределенное поведение в C ++, а не определенный тип ловушек.
Язык Си не имеет никакой концепции захвата, если только вы не подсчитываете сигналы.
В C ++ есть принцип разработки, согласно которому он не вводит накладные расходы, отсутствующие в C, если вы не попросите об этом. Таким образом, Страуструп не хотел бы предписывать, чтобы целые числа вели себя так, что требует какой-либо явной проверки.
Некоторые ранние компиляторы и облегченные реализации для ограниченного оборудования вообще не поддерживают исключения, и исключения часто можно отключить с помощью параметров компилятора. Обязательные исключения для встроенных языков были бы проблематичными.
Даже если бы C ++ сделал проверку целых чисел, 99% программистов в ранние дни отключили бы для повышения производительности ...
бенчмаркинг из коробки.
Когда C # был новым, Microsoft надеялась, что многие разработчики C ++ перейдут на него. Они знали, что многие люди C ++ думали о C ++ как о быстром, особенно быстрее, чем языки, которые "впустую" время на автоматическое управление памятью и тому подобное.
И потенциальные последователи, и рецензенты журналов, вероятно, получат копию нового C #, установят его, создадут тривиальное приложение, которое никто никогда не напишет в реальном мире, запустят его в тесном цикле и измерят, сколько времени это займет. Затем они принимают решение для своей компании или публикуют статью, основанную на этом результате.
Тот факт, что их тест показал, что C # медленнее, чем изначально скомпилированный C ++, - это то, что быстро отключает людей из C #. Тот факт, что ваше приложение на C # будет автоматически распознавать переполнение / переполнение, может пропустить. Таким образом, он выключен по умолчанию.
Я думаю, что очевидно, что 99% времени мы хотим / проверяем, чтобы оно было включено. Это неудачный компромисс.
readonly rect foo;
Error: User Rate Limit ExceededDoSomething(foo.X, foo.Y
Error: User Rate Limit Exceededfoo
Error: User Rate Limit ExceededX
Error: User Rate Limit Exceededfoo
Error: User Rate Limit ExceededY
Error: User Rate Limit Exceeded
/checked
Error: User Rate Limit Exceeded
почему ошибки не будут возникать по умолчанию во время выполнения, сводится к наследию желания создавать языки программирования с ACID-подобным поведением. В частности, принцип, что все, что вы кодируете, должен делать (или не кодировать), он будет делать (или не делать). Если вы не кодировали какой-либо обработчик ошибок, то компьютер "примет" благодаря отсутствию обработчика ошибок вы действительно хотите сделать нелепую, подверженную сбоям вещь, о которой вы говорите.
(Кислотная ссылка:http://en.wikipedia.org/wiki/ACID)
что целочисленное переполнение - это всегда нежелательное поведение.
Иногда целочисленное переполнение - желаемое поведение. Один пример, который я видел, представляет собой представление абсолютного значения заголовка в виде числа с фиксированной точкой. С учетом целого без знака 0 равно 0 или 360 градусов, а максимальное 32-разрядное целое число без знака (0xffffffff) является наибольшим значением чуть ниже 360 градусов.
int main()
{
uint32_t shipsHeadingInDegrees= 0;
// Rotate by a bunch of degrees
shipsHeadingInDegrees += 0x80000000; // 180 degrees
shipsHeadingInDegrees += 0x80000000; // another 180 degrees, overflows
shipsHeadingInDegrees += 0x80000000; // another 180 degrees
// Ships heading now will be 180 degrees
cout << "Ships Heading Is" << (double(shipsHeadingInDegrees) / double(0xffffffff)) * 360.0 << std::endl;
}
Вероятно, в других ситуациях переполнение допустимо, аналогично этому примеру.