Я не совсем понимаю, почему я не получаю исключение деления на ноль:
int d = 0;
d /= d;
Я ожидал получить деление на нулевое исключение, но вместо этого d == 1
.
Почему не d /= d
генерирует исключение деления на ноль, когда d == 0
?
Я не совсем понимаю, почему я не получаю исключение деления на ноль:
int d = 0;
d /= d;
Я ожидал получить деление на нулевое исключение, но вместо этого d == 1
.
Почему не d /= d
генерирует исключение деления на ноль, когда d == 0
?
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()
in if (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