Nie do końca rozumiem, dlaczego nie otrzymuję wyjątku dzielenia przez zero:
int d = 0;
d /= d;
Spodziewałem się, że otrzymam wyjątek dzielenia przez zero, ale zamiast tego d == 1
.
Dlaczego nie d /= d
zgłasza wyjątku dzielenia przez zero, kiedy d == 0
?
c++
compiler-optimization
undefined-behavior
division
divide-by-zero
Valerii Boldakov
źródło
źródło
throw
instrukcję. Nic więcej (chyba że jesteś w krainie nieokreślonych zachowań).Odpowiedzi:
C ++ nie ma wyjątku „Dzielenie przez zero” do przechwycenia. Zachowanie, które obserwujesz, jest wynikiem optymalizacji kompilatora:
d == 0
) nie mogą wystąpićd / d
zawsze musi wynosić 1.Jednak...
Możemy zmusić kompilator do wywołania „prawdziwego” dzielenia przez zero z niewielkimi zmianami w kodzie.
volatile int d = 0; d /= d; //What happens?
Więc teraz pozostaje pytanie: teraz, kiedy w zasadzie zmusiliśmy kompilator, aby pozwolił na to, co się dzieje? Jest to niezdefiniowane zachowanie - ale teraz uniemożliwiliśmy kompilatorowi optymalizację wokół tego niezdefiniowanego zachowania.
W większości zależy to od środowiska docelowego. To nie wyzwoli wyjątku programowego, ale może (w zależności od docelowego procesora) wyzwolić wyjątek sprzętowy (Integer-Divide-by-zero), którego nie można przechwycić w tradycyjny sposób, można przechwycić wyjątek programowy. Tak jest z pewnością w przypadku procesora x86 i większości innych (ale nie wszystkich!) Architektur.
Istnieją jednak metody radzenia sobie z wyjątkiem sprzętowym (jeśli wystąpi) zamiast po prostu pozwolić na awarię programu: spójrz na ten post, aby znaleźć kilka metod, które mogą mieć zastosowanie: Wyłapywanie wyjątku: dzielenie przez zero . Zauważ, że różnią się one od kompilatora do kompilatora.
źródło
1
jest czymś całkowicie ważnym. Uzyskanie 14684554 musi wynikać z tego, że kompilator optymalizuje jeszcze bardziej - propagujed==0
warunek początkowy i dlatego może stwierdzić nie tylko „to jest 1 lub UB”, ale w rzeczywistości „to jest UB, kropka”. Dlatego nawet nie zawraca sobie głowy tworzeniem kodu, który ładuje stałą1
.Aby uzupełnić inne odpowiedzi, fakt, że dzielenie przez zero jest niezdefiniowanym zachowaniem, oznacza, że kompilator może zrobić wszystko w przypadkach, w których tak się stanie:
0 / 0 == 1
i odpowiednio zoptymalizować. Wydaje się, że właśnie to tutaj zrobiło.0 / 0 == 42
i ustawićd
na tę wartość.d
jest nieokreślona, a tym samym pozostawić zmienną niezainicjowaną, tak aby jej wartość była tym, co zostało wcześniej zapisane w przydzielonej jej pamięci. Niektóre z nieoczekiwanych wartości obserwowanych w komentarzach w innych kompilatorach mogą być spowodowane przez te kompilatory, które robią coś takiego.d
nie była znana w czasie kompilacji, kompilator nadal może założyć, że nigdy nie jest równa zero i odpowiednio zoptymalizować kod. W konkretnym przypadku kodu OP jest to praktycznie nie do odróżnienia od kompilatora, który po prostu tak założył0 / 0 == 1
, ale kompilator może również, na przykład, założyć, żeputs()
inif (d == 0) puts("About to divide by zero!"); d /= d;
nigdy nie zostanie wykonany!źródło
Zachowanie dzielenia liczby całkowitej przez zero jest niezdefiniowane w standardzie C ++. Jest nie wymaga się wyjątek.
(Dzielenie zmiennoprzecinkowe przez zero jest również niezdefiniowane, ale definiuje to IEEE754).
Twój kompilator optymalizuje
d /= d
się,d = 1
co jest rozsądnym wyborem. Dozwolone jest dokonanie tej optymalizacji, ponieważ można założyć, że w kodzie nie ma niezdefiniowanego zachowania - tod
nie może wynosić zero.źródło
d
nie może być zero”, czy też zakładasz, że kompilator nie widzi linii:int d = 0;
?? :)Zwróć uwagę, że możesz sprawić, by kod wygenerował wyjątek C ++ w tym (i innych przypadkach) przy użyciu bezpiecznych liczb zwiększonych. https://github.com/boostorg/safe_numerics
źródło