Ich habe diese Frage zu SO gelesen , in der ein allgemeines undefiniertes Verhalten in C ++ erörtert wird, und ich habe mich gefragt: Hat Java auch ein undefiniertes Verhalten?
Wenn dies der Fall ist, was sind dann einige häufige Ursachen für undefiniertes Verhalten in Java?
Wenn nicht, welche Features von Java machen es dann frei von solchen Verhaltensweisen und warum wurden die neuesten Versionen von C und C ++ nicht mit diesen Eigenschaften implementiert?
java
c++
c
undefined-behavior
Acht
quelle
quelle
Antworten:
In Java können Sie das Verhalten eines falsch synchronisierten Programms als undefiniert betrachten.
Java 7 JLS verwendet das Wort "undefined" in 17.4.8 einmal. Ausführungs- und Kausalitätsanforderungen :
In der Java-API-Dokumentation werden einige Fälle angegeben, in denen die Ergebnisse nicht definiert sind, z. B. im (veralteten) Konstruktor Datum (int Jahr, int Monat, int Tag) :
Javadocs für ExecutorService.invokeAll (Collection) -Status :
Weniger formales "undefiniertes" Verhalten ist beispielsweise in ConcurrentModificationException zu finden , wo API-Dokumente den Begriff "best effort" verwenden:
Blinddarm
Eines der Frage Kommentare bezieht sich auf einen Artikel von Eric Lippert , die in Thema Fragen hilfreiche Einführung bietet: die Implementierung definiert Verhalten .
Ich empfehle diesen Artikel für sprachunabhängige Überlegungen, obwohl es sich zu bedenken lohnt, dass der Autor auf C # und nicht auf Java abzielt.
Oben ist nur eine sehr kurze Darstellung; Der vollständige Artikel enthält Erläuterungen und Beispiele zu den in diesem Auszug genannten Punkten. es ist viel lesenswert. Zum Beispiel können Details, die für den "sechsten Faktor" angegeben wurden, einen Einblick in die Motivation für viele Aussagen im Java Memory Model ( JSR 133 ) geben und dabei helfen, zu verstehen, warum einige Optimierungen zulässig sind, was zu undefiniertem Verhalten führt, während andere verboten sind und dazu führen Einschränkungen wie Vorgängervorgänge und Kausalitätsanforderungen .
quelle
Ich glaube nicht, dass es in Java ein undefiniertes Verhalten gibt, zumindest nicht im gleichen Sinne wie in C ++.
Der Grund dafür ist, dass Java eine andere Philosophie hat als C ++. Ein zentrales Entwurfsziel von Java bestand darin, dass Programme plattformübergreifend unverändert ausgeführt werden können. Daher definiert die Spezifikation alles sehr explizit.
Im Gegensatz dazu ist Effizienz ein zentrales Entwurfsziel von C und C ++: Es sollte keine Funktionen (einschließlich Plattformunabhängigkeit) geben, die die Leistung beeinträchtigen, auch wenn Sie sie nicht benötigen. Zu diesem Zweck werden in der Spezifikation einige Verhaltensweisen bewusst nicht definiert, da deren Definition auf einigen Plattformen zusätzliche Arbeit verursachen und somit die Leistung verringern würde, selbst für Personen, die Programme speziell für eine Plattform schreiben und alle ihre Eigenheiten kennen.
Es gibt sogar ein Beispiel, in dem Java aus genau diesem Grund gezwungen war, eine begrenzte Form von undefiniertem Verhalten rückwirkend einzuführen: Das Schlüsselwort strictfp wurde in Java 1.2 eingeführt, damit Gleitkommaberechnungen nicht genau dem IEEE 754-Standard folgen, wie es die Spezifikation zuvor gefordert hatte Dies erforderte zusätzlichen Arbeitsaufwand und verlangsamte alle Gleitkommaberechnungen auf einigen gängigen CPUs, wobei in einigen Fällen sogar schlechtere Ergebnisse erzielt wurden.
quelle
int x=-1; foo(); x<<=1;
hypermodernen Philosophie würde es vorziehen, neu zu schreiben,foo
sodass jeder Pfad, der nicht endet, nicht erreichbar sein muss. Dies, wennfoo
es sich umif (should_launch_missiles) { launch_missiles(); exit(1); }
einen Compiler handelt, könnte (und sollte nach Ansicht mancher Leute) dies einfach vereinfachenlaunch_missiles(); exit(1);
. Die traditionelle UB war zufällige Codeausführung, aber diese war früher an die Gesetze der Zeit und der Kausalität gebunden. New verbesserte UB ist von beiden nicht gebunden.Java ist sehr bemüht, undefiniertes Verhalten auszurotten, gerade wegen der Lehren aus früheren Sprachen. Beispielsweise werden Variablen auf Klassenebene automatisch initialisiert. lokale Variablen werden aus Performancegründen nicht automatisch initialisiert, es gibt jedoch eine ausgeklügelte Datenflussanalyse, um zu verhindern, dass jemand ein Programm schreibt, das dies erkennen kann. Verweise sind keine Zeiger, daher können keine ungültigen Verweise vorhanden sein, und die Dereferenzierung
null
führt zu einer bestimmten Ausnahme.Natürlich gibt es einige Verhaltensweisen, die nicht vollständig spezifiziert sind, und Sie können unzuverlässige Programme schreiben, wenn Sie davon ausgehen, dass dies der Fall ist. Wenn Sie beispielsweise über ein normales (nicht sortiertes)
Set
Element iterieren , garantiert die Sprache, dass Sie jedes Element genau einmal sehen, jedoch nicht in welcher Reihenfolge. Die Reihenfolge kann bei aufeinanderfolgenden Läufen gleich sein oder sich ändern. oder es kann gleich bleiben, solange keine anderen Zuordnungen vorgenommen werden, oder solange Sie Ihr JDK nicht aktualisieren usw. Es ist nahezu unmöglich, alle derartigen Effekte zu beseitigen. Zum Beispiel müssten Sie alle Kollektionsoperationen explizit anordnen oder randomisieren , und das ist die kleine zusätzliche Unbestimmtheit einfach nicht wert.quelle
Sie müssen das "Undefinierte Verhalten" und seinen Ursprung verstehen.
Undefiniertes Verhalten bedeutet ein Verhalten, das nicht durch die Standards definiert ist. C / C ++ hat zu viele verschiedene Compiler-Implementierungen und zusätzliche Funktionen. Diese zusätzlichen Funktionen banden den Code an den Compiler. Dies lag daran, dass es keine zentralisierte Sprachentwicklung gab. So wurden einige der erweiterten Funktionen einiger Compiler zu undefinierten Verhaltensweisen.
Während in Java die Sprachspezifikation von Sun-Oracle gesteuert wird und niemand sonst versucht, Spezifikationen und damit keine undefinierten Verhaltensweisen festzulegen.
Bearbeitet Speziell die Beantwortung der Frage
quelle
Java beseitigt im Wesentlichen das gesamte undefinierte Verhalten in C / C ++. (Zum Beispiel: Überlauf von Ganzzahlen mit Vorzeichen, Division durch Null, nicht initialisierte Variablen, Null-Zeiger-Dereferenzierung, Verschiebung um mehr als die Bitbreite, doppelte Freiheit, sogar "keine neue Zeile am Ende des Quellcodes") werden selten von Programmierern angetroffen.
Java Native Interface (JNI), eine Möglichkeit für Java, C- oder C ++ - Code aufzurufen. Es gibt viele Möglichkeiten, Fehler in JNI zu machen, z. B. die Funktionssignatur falsch zu machen, ungültige Aufrufe an JVM-Services auszuführen, Speicher zu beschädigen, Inhalte falsch zuzuweisen / freizugeben und vieles mehr. Ich habe diese Fehler bereits gemacht und im Allgemeinen stürzt die gesamte JVM ab, wenn ein Thread, der JNI-Code ausführt, einen Fehler feststellt.
Thread.stop()
, die veraltet ist. Zitat:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
quelle