Warum wird int i = 1024 * 1024 * 1024 * 1024 ohne Fehler kompiliert?

Die Grenze von int liegt zwischen -2147483648 und 2147483647.

Wenn ich eingabe

 int i = 2147483648; 

dann wird Eclipse eine rote Unterstreichung unter “2147483648” anzeigen.

Aber wenn ich das tue:

 int i = 1024 * 1024 * 1024 * 1024; 

es wird gut kompilieren.

 public class Test { public static void main(String[] args) { int i = 2147483648; // error int j = 1024 * 1024 * 1024 * 1024; // no error } } 

Vielleicht ist es eine grundlegende Frage in Java, aber ich habe keine Ahnung, warum die zweite Variante keinen Fehler erzeugt.

Mit dieser Aussage ist nichts falsch; du multiplizierst einfach 4 Zahlen und weist sie einem int zu, da ist zufällig ein Überlauf. Dies unterscheidet sich von der Zuweisung eines einzelnen Literals , das zur Kompilierungszeit durch ein Häkchen überprüft wird.

Es ist das Out-of-Bound- Literal , das den Fehler verursacht, nicht die Zuweisung :

 System.out.println(2147483648); // error System.out.println(2147483647 + 1); // no error 

Im Gegensatz dazu würde ein long Literal gut kompilieren:

 System.out.println(2147483648L); // no error 

Beachten Sie, dass das Ergebnis tatsächlich noch zur Kompilierzeit berechnet wird, da 1024 * 1024 * 1024 * 1024 ein konstanter Ausdruck ist :

 int i = 1024 * 1024 * 1024 * 1024; 

wird:

  0: iconst_0 1: istore_1 

Beachten Sie, dass das Ergebnis ( 0 ) einfach geladen und gespeichert wird und keine Multiplikation stattfindet.


Aus JLS §3.10.1 (Danke an @ChrisK für das Erwecken in den Kommentaren):

Es ist ein Kompilierungserrors, wenn ein Dezimal-Literal vom Typ int größer als 2147483648 (2 31 ) ist oder wenn das Dezimal-Literal 2147483648 anderen Stelle als dem Operanden des unären Minus-Operators ( §15.15.4 ) erscheint.

1024 * 1024 * 1024 * 1024 und 2147483648 haben in Java nicht denselben Wert.

Eigentlich 2147483648 nicht einmal ein Wert (obwohl 2147483648L ist) in Java. Der Compiler weiß buchstäblich nicht, was es ist oder wie es zu verwenden ist. So wimmert es.

1024 ist ein gültiges int in Java und ein gültiges int multipliziert mit einem anderen gültigen int ist immer ein gültiges int . Auch wenn es nicht derselbe Wert ist, den Sie intuitiv erwarten würden, weil die Berechnung überläuft.

Beispiel

Betrachten Sie das folgende Codebeispiel:

 public static void main(String[] args) { int a = 1024; int b = a * a * a * a; } 

Würden Sie damit einen Kompilierungserrors erwarten? Es wird jetzt ein bisschen rutschiger.
Was, wenn wir eine Schleife mit 3 Iterationen machen und in der Schleife multiplizieren?

Der Compiler darf optimieren, kann aber das Verhalten des Programms dabei nicht ändern.


Einige Informationen darüber, wie dieser Fall tatsächlich gehandhabt wird:

In Java und vielen anderen Sprachen bestehen Ganzzahlen aus einer festen Anzahl von Bits. Berechnungen, die nicht in die angegebene Anzahl von Bits passen, werden überlaufen ; Die Berechnung wird im Grunde Modul 2 ^ 32 in Java durchgeführt, wonach der Wert zurück in eine vorzeichenbehaftete ganze Zahl umgewandelt wird.

Andere Sprachen oder APIs verwenden eine dynamische Anzahl von Bits ( BigInteger in Java), lösen eine Ausnahme aus oder setzen den Wert auf einen magischen Wert, wie z. B. eine Nicht-Nummer.

Ich habe keine Ahnung, warum die zweite Variante keinen Fehler erzeugt.

Das von Ihnen vorgeschlagene Verhalten, dh die Erstellung einer Diagnosemeldung, wenn eine Berechnung einen Wert erzeugt, der größer ist als der größte Wert, der in einer ganzen Zahl gespeichert werden kann, ist ein Feature . Damit Sie jede function verwenden können, muss die function als eine gute Idee betrachtet, entworfen, spezifiziert, implementiert, getestet, dokumentiert und an Benutzer gesendet werden.

Für Java ist eines oder mehrere der Dinge auf dieser Liste nicht aufgetreten, und daher verfügen Sie nicht über die function. Ich weiß nicht welche; Sie müssten einen Java-Designer fragen.

Für C # sind all diese Dinge passiert – vor ungefähr vierzehn Jahren – und so hat das entsprechende Programm in C # seit C # 1.0 einen Fehler erzeugt.

Zusätzlich zu Arshajjiis Antwort möchte ich noch etwas zeigen:

Es ist nicht die Zuweisung , die den Fehler verursacht, sondern einfach die Verwendung des Literals . Wenn du es versuchst

 long i = 2147483648; 

Sie werden feststellen, dass es auch einen Kompiliererrors verursacht, da die rechte Seite immer noch int -literal und außerhalb des Bereichs liegt.

So können Operationen mit int Werten (einschließlich Zuweisungen) ohne Kompiliererrors (und auch ohne Laufzeiterrors) überlaufen, aber der Compiler kann diese zu großen Literale nicht verarbeiten.

A: Weil es kein Fehler ist.

Hintergrund: Die Multiplikation 1024 * 1024 * 1024 * 1024 führt zu einem Überlauf. Ein Überlauf ist sehr oft ein Fehler. Unterschiedliche Programmiersprachen erzeugen ein unterschiedliches Verhalten, wenn Überläufe auftreten. Zum Beispiel, C und C ++ nennen es “undefiniertes Verhalten” für vorzeichenbehaftete Ganzzahlen, und das Verhalten ist vorzeichenlose Integer (nehmen Sie das mathematische Ergebnis, add UINT_MAX + 1 , solange das Ergebnis negativ ist, subtrahieren UINT_MAX + 1 so lang wie das Ergebnis ist größer als UINT_MAX ).

Wenn im Fall von Java das Ergebnis einer Operation mit int Werten nicht im erlaubten Bereich liegt, addiert oder subtrahiert Java das Konzept 2 ^ 32, bis das Ergebnis im erlaubten Bereich liegt. Die Aussage ist also völlig legal und nicht errorshaft. Es bringt einfach nicht das Ergebnis, das Sie sich erhofft haben.

Sie können sicherlich argumentieren, ob dieses Verhalten hilfreich ist und ob der Compiler Sie warnen sollte. Ich würde persönlich sagen, dass eine Warnung sehr nützlich wäre, aber ein Fehler wäre falsch, da es sich um legales Java handelt.