Signed Integer overflow
Переполнение знаковых целых чисел - всегда было и остается болью в левой булке. Раньше даже стандартом не было определено, каким образом отрицательные числа хранились бы в памяти. Однако с приходом С++20 мы можем смело утверждать, что стандартом разрешено единственное представление отрицательных чисел - дополнительный код или two's complement по-жидоанглосаксонски. Казалось бы, мы теперь знаем наверняка, что будет происходить с битиками при любых видах операций. Так давайте снимем клеймо позора с переполнения знаковых интов. Однако не все так просто оказывается.
С приходом С++20 только переполнение знаковых чисел вследствие преобразования стало определенным по стандарту поведением. Теперь говорится, что, если результирующий тип преобразование - знаковый, то значение переменной никак не изменяется, если исходное число может быть представлено в результирующем типе без потерь.
В обратном случае, если исходное число не может быть представлено в результирующем типе, то результирующим значением будет являться остаток от деления исходного значения по модулю 2^N, где N - количество бит, которое занимает результирующий тип. То есть результат будет получаться просто откидыванием лишних наиболее значащих бит и все!
Однако переполнение знаковых интов вследствие арифметических операций до сих пор является неопределенным поведением!(возмутительно восклицаю). Однако сколько бы возмущений не было, все упирается в конкретные причины. Я подумал вот о каких:
👉🏿 Переносимость. Разные системы работают по разным принципам и UB помогает поддерживать все системы оптимальным образом. Мы могли бы сказать, что пусть переполнение знаковых интов работает также как и переполнение беззнаковых. То есть получалось бы просто совершенно другое неожиданное (ожидаемое с точки зрения стандарта, но неожиданное для нас при запуске программы) значение. Однако некоторые системы просто напросто не продуцируют это "неправильное значение". Например, процессоры MIPS генерируют CPU exception при знаковом переполнении. Для обработки этих исключений и получения стандартного поведения было бы потрачено слишком много ресурсов.
👉🏿 Оптимизации. Неопределенное поведение позволяет компиляторам предположить, что переполнения не произойдет, и оптимизировать код. Действительно, если УБ - так плохо и об этом знают все, то можно предположить, что никто этого не допустит. Тогда компилятор может заняться своим любимым делом - оптимизировать все на свете.
Очень простой пример: когда происходит сравнение a - 10 < b -10, то компилятор может просто убрать вычитание и тогда переполнения не будет и все пойдет, как ожидается.
Так что УБ оставляет некий коридор свободы, благодаря которому могут существовать разные сценарии обработки переполнения: от полного его игнора до включения процессором "сирены", что произошло что-то очень плохое.
Leave room for uncertainty in life. Stay cool.
#cpp20 #compiler #cppcore
Post #450
4.8K