Czytałem to pytanie w SO, które omawia niektóre typowe niezdefiniowane zachowania w C ++ i zastanawiałem się: czy Java ma również niezdefiniowane zachowanie?
Jeśli tak, to jakie są najczęstsze przyczyny nieokreślonego zachowania w Javie?
Jeśli nie, to jakie funkcje Javy uwalniają go od takich zachowań i dlaczego najnowsze wersje C i C ++ nie zostały zaimplementowane z tymi właściwościami?
java
c++
c
undefined-behavior
Osiem
źródło
źródło
Odpowiedzi:
W Javie zachowanie niepoprawnie zsynchronizowanego programu można uznać za niezdefiniowane.
JLS Java 7 używa słowa „niezdefiniowany” jeden raz, w 17.4.8. Wymogi wykonania i związku przyczynowego :
Dokumentacja interfejsu API Java określa niektóre przypadki, w których wyniki są niezdefiniowane - na przykład w (przestarzałym) Konstruktorze Data (int rok, int miesiąc, int dzień) :
Stan Javadocs dla ExecutorService.invokeAll (kolekcja) :
Mniej formalny rodzaj „niezdefiniowanego” zachowania można znaleźć na przykład w ConcurrentModificationException , gdzie dokumenty API używają terminu „najlepszy wysiłek”:
dodatek
Jeden z komentarzy do pytania odnosi się do artykułu Erica Lipperta, który zawiera pomocne wprowadzenie do zagadnień tematycznych: zachowanie zdefiniowane w ramach implementacji .
Polecam ten artykuł do rozumowania niezależnego od języka, chociaż warto pamiętać, że autor celuje w C #, a nie w Javę.
Powyżej jest tylko bardzo krótki opis; pełny artykuł zawiera wyjaśnienia i przykłady punktów wymienionych w tym fragmencie; jest wiele warte czytania. Na przykład szczegóły podane dla „szóstego czynnika” mogą dać wgląd w motywację wielu stwierdzeń w Java Memory Model ( JSR 133 ), pomagając zrozumieć, dlaczego niektóre optymalizacje są dozwolone, prowadząc do nieokreślonego zachowania, podczas gdy inne są zabronione, co prowadzi do ograniczenia, takie jak wcześniejsze zdarzenia i wymagania dotyczące przyczynowości .
źródło
Z wierzchu głowy nie sądzę, że w Javie istnieje jakieś nieokreślone zachowanie, a przynajmniej nie w tym samym sensie, co w C ++.
Powodem tego jest to, że za Javą kryje się inna filozofia niż za C ++. Podstawowym celem projektowania Javy było umożliwienie uruchamiania programów bez zmian na różnych platformach, więc specyfikacja określa wszystko bardzo wyraźnie.
Natomiast głównym celem projektowania C i C ++ jest wydajność: nie powinno być żadnych funkcji (w tym niezależności od platformy), które kosztowałyby wydajność, nawet jeśli nie byłyby potrzebne. W tym celu specyfikacja celowo nie definiuje niektórych zachowań, ponieważ ich zdefiniowanie spowodowałoby dodatkową pracę na niektórych platformach, a tym samym zmniejszyłoby wydajność nawet dla osób, które piszą programy specjalnie dla jednej platformy i są świadome wszystkich jej osobliwości.
Istnieje nawet przykład, w którym Java była zmuszona z mocą wsteczną wprowadzić ograniczoną formę nieokreślonego zachowania z tego właśnie powodu: słowo kluczowe strictfp zostało wprowadzone w Javie 1.2, aby umożliwić obliczenia zmiennoprzecinkowe odbiegające dokładnie od standardu IEEE 754, jak wcześniej wymagała specyfikacja , ponieważ zrobienie tego wymagało dodatkowej pracy i spowolniło wszystkie obliczenia zmiennoprzecinkowe na niektórych popularnych procesorach, aw niektórych przypadkach powodując gorsze wyniki.
źródło
int x=-1; foo(); x<<=1;
hipernowoczesną filozofię, sprzyjałoby przepisywaniefoo
, aby każda ścieżka, która nie wychodzi, musiała być nieosiągalna. To, jeślifoo
jestif (should_launch_missiles) { launch_missiles(); exit(1); }
kompilatorem, może (i zdaniem niektórych osób powinno) uprościć to po prostulaunch_missiles(); exit(1);
. Tradycyjny UB polegał na losowym wykonywaniu kodu, ale kiedyś obowiązywały go prawa czasu i przyczynowości. Nowy ulepszony UB nie jest związany z żadnym.Java bardzo mocno stara się wytępić niezdefiniowane zachowanie, właśnie z powodu lekcji wcześniejszych języków. Na przykład zmienne na poziomie klasy są inicjowane automatycznie; zmienne lokalne nie są automatycznie inicjowane ze względu na wydajność, ale istnieje zaawansowana analiza przepływu danych, aby uniemożliwić każdemu napisanie programu, który byłby w stanie to wykryć. Referencje nie są wskaźnikami, więc nieprawidłowe referencje nie mogą istnieć, a dereferencje
null
powodują określony wyjątek.Oczywiście istnieją pewne zachowania, które nie są w pełni określone, i możesz pisać niewiarygodne programy, jeśli się zorientujesz. Na przykład, jeśli wykonujesz iterację normalną (nieposortowaną)
Set
, język gwarantuje, że zobaczysz każdy element dokładnie raz, ale nie w kolejności, w jakiej je zobaczysz. Kolejność może być taka sama przy kolejnych uruchomieniach lub może ulec zmianie; lub może pozostać taki sam, o ile nie wystąpią żadne inne alokacje lub dopóki nie zaktualizujesz JDK itp. Pozbycie się wszystkich takich efektów jest prawie niemożliwe ; na przykład musiałbyś jawnie zamówić lub randomizować wszystkie operacje Kolekcje, a to po prostu nie jest warte małej dodatkowej, niezdefiniowanej.źródło
Musisz zrozumieć „Niezdefiniowane zachowanie” i jego pochodzenie.
Niezdefiniowane zachowanie oznacza zachowanie, które nie jest zdefiniowane przez standardy. C / C ++ ma zbyt wiele różnych implementacji kompilatora i dodatkowe funkcje. Te dodatkowe funkcje wiązały kod z kompilatorem. Stało się tak, ponieważ nie było scentralizowanego rozwoju języka. Tak więc niektóre zaawansowane funkcje niektórych kompilatorów stały się „niezdefiniowanymi zachowaniami”.
Podczas gdy w Javie specyfikacja języka jest kontrolowana przez Sun-Oracle i nikt inny nie próbuje tworzyć specyfikacji, a tym samym nie ma niezdefiniowanych zachowań.
Zredagowano specjalnie odpowiadając na pytanie
źródło
Java zasadniczo eliminuje wszystkie niezdefiniowane zachowania występujące w C / C ++. (Na przykład: przepełnienie liczby całkowitej ze znakiem, dzielenie przez zero, niezainicjowane zmienne, zerowe wskazanie wskaźnika, przesunięcie o więcej niż szerokość bitów, podwójne zwolnienie, nawet „brak nowej linii na końcu kodu źródłowego”.) Ale Java ma kilka niejasnych, niezdefiniowanych zachowań, które są rzadko spotykane przez programistów.
Java Native Interface (JNI), sposób, w jaki Java może wywoływać kod C lub C ++. Istnieje wiele sposobów na zepsucie JNI, takich jak błędne podpisanie funkcji, wykonywanie nieprawidłowych wywołań do usług JVM, niszczenie pamięci, niepoprawne przydzielanie / zwalnianie rzeczy i wiele innych. Popełniłem już te błędy i ogólnie cała JVM ulega awarii, gdy dowolny wątek wykonujący kod JNI popełnia błąd.
Thread.stop()
, co jest przestarzałe. Zacytować:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
źródło