Я не совсем понимаю, почему я не получаю исключение деления на ноль:
int d = 0;
d /= d;
Я ожидал получить деление на нулевое исключение, но вместо этого d == 1
.
Почему не d /= d
генерирует исключение деления на ноль, когда d == 0
?
c++
compiler-optimization
undefined-behavior
division
divide-by-zero
Валерий Болдаков
источник
источник
throw
оператором. Ничего другого (если только вы не находитесь в стране неопределенного поведения).Ответы:
В C ++ нет исключения «Деление на ноль», которое можно было бы перехватить. Наблюдаемое вами поведение является результатом оптимизации компилятора:
d == 0
) не должны выполняться.d / d
всегда должно быть равно 1.Тем не мение...
Мы можем заставить компилятор запускать «настоящее» деление на ноль с незначительной настройкой вашего кода.
volatile int d = 0; d /= d; //What happens?
Итак, остается вопрос: теперь, когда мы в основном заставили компилятор позволить этому случиться, что происходит? Это неопределенное поведение, но мы не позволили компилятору оптимизировать это неопределенное поведение.
В основном это зависит от целевой среды. Это не вызовет программного исключения, но может (в зависимости от целевого ЦП) вызвать аппаратное исключение (целочисленное деление на ноль), которое не может быть обнаружено традиционным способом, так как может быть обнаружено программное исключение. Это определенно верно для ЦП x86 и большинства других (но не всех!) Архитектур.
Однако существуют методы обработки аппаратного исключения (если оно возникает) вместо того, чтобы просто дать программе сбой: посмотрите этот пост, чтобы узнать о некоторых методах, которые могут быть применимы: Перехват исключения: разделите на ноль . Обратите внимание, что они различаются от компилятора к компилятору.
источник
1
- это что угодно. Получение 14684554 должно происходить из-за того, что компилятор оптимизирует еще больше - он распространяет начальноеd==0
условие и поэтому может заключить не только «это либо 1, либо UB», но фактически «это UB, точка». Поэтому не нужно даже создавать код, загружающий константу1
.Чтобы дополнить другие ответы, тот факт, что деление на ноль является неопределенным поведением, означает, что компилятор может делать что угодно в тех случаях, когда это произойдет:
0 / 0 == 1
и соответствующим образом оптимизировать. По сути, это именно то, что он здесь сделал.0 / 0 == 42
и установитьd
это значение.d
является неопределенным, и, таким образом, оставить переменную неинициализированной, так что ее значение будет тем, что было ранее записано в выделенную для нее память. Некоторые из неожиданных значений, наблюдаемых в комментариях для других компиляторов, могут быть вызваны тем, что эти компиляторы делают что-то вроде этого.d
не было известно во время компиляции, компилятор все равно мог бы предположить, что оно никогда не равно нулю, и соответствующим образом оптимизировать код. В конкретном случае кода OP это практически неотличимо от компилятора, просто предполагая это0 / 0 == 1
, но компилятор также может, например, предположить, чтоputs()
inif (d == 0) puts("About to divide by zero!"); d /= d;
никогда не выполняется!источник
Поведение целочисленного деления на ноль не определено стандартом C ++. Это не обязательно бросать исключение.
(Деление с плавающей запятой на ноль также не определено, но IEEE754 определяет это.)
Ваш компилятор оптимизируется
d /= d
под,d = 1
что является разумным выбором. Эту оптимизацию можно проводить, поскольку можно предположить, что в вашем коде нет неопределенного поведения - этоd
не может быть нулевым.источник
d
не может быть нулем", вы также предполагаете, что компилятор не видит строку:int d = 0;
?? :)Обратите внимание, что в этом (и в других случаях) код может генерировать исключение C ++, используя числовые значения с повышенной безопасностью. https://github.com/boostorg/safe_numerics
источник