#опытным
Сколько будет х/х?
Как ни странно, на этот вопрос есть множество ответов.
Математический ответ: при x != 0 выражение вырождается в единицу. Если же x = 0, то значение выражения неопределено. x=0 приводит к неопределенности вида 0/0, у которого нет корректного значения.
Какой же ответ у С++?
#include <iostream>
int main() {
int x = 0;
std::cout << x/x << std::endl;
}
Ответ будет 1!
На те вот ссылочку на годболт, если не верите.
#ЧЗХ? Компьютер считать чтоли не умеет?
По ссылочке на самом деле только вывод gcc показан. И он действительно выдает 1.
Но на кланге при запуске программа получает сигнал SIGFPE, а на msvc выдается вот такая цифирь 3221225620.
Понятное дело, что деление на 0 - это UB, и это явно видно по результатам запуска на разных компиляторах.
Если вы думали, что проблема проявляется только оптимизациях - нет. Даже на -O0 все равно gcc единичку выдает.
Тогда как могла появиться единичка?
С какой-то стороны этот вопрос не имеет смысла, потому что при UB компилятор волен поступать, как ему вздумается. Но этот конкретный пример показывает одну из популярных моделей поведения компилятора при UB.
С точки зрения разработчика С++ UB - это скорее плохо. Некоторые из нас идут на риск, когда понимают реальный эффект на конкретном компиляторе и железе, но в большинстве кейсов мы стараемся избегать UB.
И компилятор это учитывает. Если UB - это плохо и программист старается его не допускать в своем коде, значит можно считать, что UB в коде не будет. И на основе этого знания компилировать программу.
UB в данном случае произойдет только при x=0. Тогда компилятор просто выкидывает этот вариант из анализа, притворяется, что такого не может быть. Тогда выражение x/x действительно становится равным единице.
Компилятор применил константную свёртку (constant folding) — одно из базовых преобразований, которое работает даже на нулевом уровне оптимизации. Он увидел выражение
x / x, где x — целочисленная переменная, и, как мы все делали в школе, сократил числитель и знаменатель. В итоге на консоль вывелась единичка.Чтобы компилятор поступал максимально честно и ничего не смог сделать с выражением, надо пометить переменную volatile.
volatile int x = 0;
std::cout << x/x << std::endl;
Это ключевое слово запрещает компилятору делать предположения о значении переменной и заставляет чего честно читать переменную из памяти.
Do not trust programmers. Stay cool.
#compiler