Eine scheinbar endlose Schleife wird beendet, es sei denn, System.out.println wird verwendet

Lesezeit: 9 Minuten

Benutzer-Avatar
Omar

Ich hatte ein einfaches Stück Code, das war soll eine Endlosschleife da sein x wird immer wachsen und immer größer bleiben als j.

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
   x = x + y;
}
System.out.println(y);

aber so wie es ist, druckt es y und läuft nicht endlos. Ich kann nicht herausfinden, warum. Wenn ich den Code jedoch wie folgt anpasse:

int x = 5;
int y = 9;
for (int j = 0; j < x; j++) {
    x = x + y;
    System.out.println(y);
}
System.out.println(y);

Es wird zu einer Endlosschleife und ich habe keine Ahnung warum. Erkennt Java eine Endlosschleife und überspringt sie in der ersten Situation, muss aber in der zweiten einen Methodenaufruf ausführen, damit es sich wie erwartet verhält? Verwirrt 🙂

  • Die zweite Schleife ist endlos, weil die obere Grenze x wächst Schneller als die Schleifenvariable j. Mit anderen Worten, j wird niemals eine Obergrenze erreichen, daher wird die Schleife “für immer” laufen. Nun, nicht für immer, irgendwann wirst du höchstwahrscheinlich einen Überlauf bekommen.

    – Tim Biegeleisen

    1. Februar 2017 um 4:53 Uhr

  • Es ist keine Endlosschleife, es dauert nur 238609294 Mal, bis die Schleife im ersten Fall aus der for-Schleife herauskommt, und beim zweiten Mal wird der Wert von ausgegeben y 238609294 mal

    – N00b Pr0grammer

    1. Februar 2017 um 4:58 Uhr

  • Ein-Wort-Antwort: Überlauf

    – qwr

    1. Februar 2017 um 6:20 Uhr

  • Amüsant, System.out.println(x) Anstatt von y am Ende hätte sofort gezeigt, was das Problem war

    – JollyJoker

    1. Februar 2017 um 10:54 Uhr

  • @TeroLahtinen nein, würde es nicht. Lesen Sie die Java-Sprachspezifikation, wenn Sie Zweifel haben, was der int-Typ ist. Es ist hardwareunabhängig.

    – 9ilsdx 9rvj 0lo

    1. Februar 2017 um 11:37 Uhr

Benutzer-Avatar
Zbynek Vyskovsky – kvr000

Beide Beispiele sind nicht endlos.

Das Problem ist die Begrenzung von int Geben Sie Java (oder so ziemlich jede andere gängige Sprache) ein. Wenn der Wert von x erreicht 0x7fffffffdas Hinzufügen eines positiven Werts führt zu einem Überlauf und dem x wird negativ, also kleiner als j.

Der Unterschied zwischen der ersten und zweiten Schleife besteht darin, dass der innere Code viel mehr Zeit in Anspruch nimmt und es wahrscheinlich mehrere Minuten dauern würde, bis er fertig ist x überläuft. Für das erste Beispiel kann es weniger als das zweite dauern oder der Code wird höchstwahrscheinlich vom Optimierer entfernt, da er keine Wirkung hat.

Wie in der Diskussion erwähnt, hängt die Zeit stark davon ab, wie das Betriebssystem die Ausgabe puffert, ob es an den Terminalemulator usw. ausgibt, sodass sie viel länger als einige Minuten sein kann.

  • Ich habe gerade ein Programm (auf meinem Laptop) ausprobiert, das eine Zeile in einer Schleife druckt. Ich habe es zeitlich festgelegt und es war in der Lage, ungefähr 1000 Zeilen/Sekunde zu drucken. Basierend auf dem Kommentar von N00b, dass die Schleife 238609294 Mal ausgeführt wird, dauert es etwa 23861 Sekunden, bis die Schleife beendet ist – über 6,6 Stunden. Ein bisschen mehr als “einige Minuten”.

    – ajb

    1. Februar 2017 um 5:29 Uhr


  • @ajb: Hängt von der Implementierung ab. IIRC println() Unter Windows handelt es sich um eine blockierende Operation, während sie unter (einigen?) Unix gepuffert ist und daher viel schneller abläuft. Versuchen Sie es auch mit print()die puffert, bis sie auf a trifft \n (oder der Puffer füllt sich, oder flush() wird genannt)

    – BlueRaja – Danny Pflughoeft

    1. Februar 2017 um 8:40 Uhr


  • Es hängt auch davon ab, welches Terminal die Ausgabe anzeigt. Ein extremes Beispiel finden Sie unter stackoverflow.com/a/21947627/53897 (bei dem die Verlangsamung auf den Zeilenumbruch zurückzuführen war).

    – Thorbjørn Ravn Andersen

    1. Februar 2017 um 17:40 Uhr

  • Ja, es ist unter UNIX gepuffert, aber es blockiert immer noch. Sobald der 8K-Puffer gefüllt ist, wird er blockiert, bis Platz vorhanden ist. Die Geschwindigkeit hängt stark davon ab, wie schnell es verbraucht wird. Die Umleitung der Ausgabe an /dev/null ist am schnellsten, aber wenn sie an das Terminal gesendet wird (Standardeinstellung), sind Grafikaktualisierungen auf dem Bildschirm und viel mehr Rechenleistung erforderlich, da die Schriftarten gerendert werden, was sie verlangsamt.

    – Pinguin359

    1. Februar 2017 um 19:59 Uhr


  • @Zbynek oh, wahrscheinlich, aber das erinnert mich daran, dass die E / A von Terminals normalerweise zeilengepuffert und nicht blockiert wird, sodass höchstwahrscheinlich jeder Druck zu einem Systemaufruf führt, der den Terminalfall weiter verlangsamt.

    – Pinguin359

    2. Februar 2017 um 4:44 Uhr

Da sie als int deklariert sind, wird die Schleife unterbrochen, sobald der maximale Wert erreicht ist, da der x-Wert negativ wird.

Aber wenn System.out.println zur Schleife hinzugefügt wird, wird die Ausführungsgeschwindigkeit sichtbar (da die Ausgabe an die Konsole die Ausführungsgeschwindigkeit verlangsamt). Wenn Sie jedoch das 2. Programm (das mit syso in der Schleife) lange genug laufen lassen, sollte es sich genauso verhalten wie das erste (das ohne syso in der Schleife).

  • Die Leute wissen nicht, wie sehr Spam an die Konsole ihren Code verlangsamen kann.

    – Benutzer9993

    1. Februar 2017 um 14:31 Uhr

Benutzer-Avatar
Ashok Vishnoi

Dafür kann es zwei Gründe geben:

  1. Java optimiert die for Schleife und da gibt es keine Verwendung von x nach der Schleife entfernt einfach die Schleife. Sie können dies überprüfen, indem Sie setzen System.out.println(x); Aussage nach der Schleife.

  2. Es ist möglich, dass Java die Schleife nicht wirklich optimiert und das Programm korrekt und schließlich ausführt x wird zu groß für int und Überlauf. Ein ganzzahliger Überlauf wird höchstwahrscheinlich die ganze Zahl machen x als negativ, das kleiner als j sein wird, und so wird es aus der Schleife herauskommen und den Wert von drucken y. Dies kann auch durch Hinzufügen überprüft werden System.out.println(x); nach der Schleife.

Auch im ersten Fall wird es schließlich zu einem Überlauf kommen, wodurch es zum zweiten Fall wird, sodass es niemals eine echte Endlosschleife sein wird.

  • Ich wähle Tür Nummer 2.

    – Robby Cornelissen

    1. Februar 2017 um 5:00 Uhr

  • WAHR. Es ging auf die negative Skala und verließ die Schleife. Aber ein sysout ist so langsam, um die Illusion einer Endlosschleife hinzuzufügen.

    – Pavan Kumar

    1. Februar 2017 um 5:08 Uhr

  • 1. Wäre ein Fehler. Compiler-Optimierungen dürfen das Verhalten eines Programms nicht ändern. Wenn dies eine Endlosschleife war, kann der Compiler alles optimieren, was er will, das Ergebnis muss jedoch immer noch eine Endlosschleife sein. Die wirkliche Lösung ist, dass sich das OP irrt: Keines der beiden ist eine Endlosschleife, das eine macht einfach mehr Arbeit als das andere, also dauert es länger.

    – Jörg W Mittag

    1. Februar 2017 um 15:09 Uhr


  • @JörgWMittag In diesem Fall ist x eine lokale Variable ohne Beziehung zu irgendetwas anderem. Daher ist es möglich, dass es wegoptimiert wird. Aber man sollte sich den Bytecode ansehen, um festzustellen, ob das der Fall ist, und niemals einfach annehmen, dass der Compiler so etwas getan hat.

    – Hoffentlich Hilfreich

    2. Februar 2017 um 9:05 Uhr


Sie sind beide keine Endlosschleifen, anfangs j = 0 , solange j < x, j steigt (j ++) und j eine Ganzzahl ist, sodass die Schleife ausgeführt wird, bis sie den Maximalwert erreicht, und dann überläuft (Ein Integer-Überlauf ist die Bedingung Dies tritt auf, wenn das Ergebnis einer arithmetischen Operation, z. B. einer Multiplikation oder Addition, die maximale Größe des Ganzzahltyps überschreitet, der zum Speichern verwendet wird.). Für das zweite Beispiel gibt das System nur den Wert von y aus, bis die Schleife unterbrochen wird.

Wenn Sie nach einem Beispiel für eine Endlosschleife suchen, sollte es so aussehen

int x = 6;

for (int i = 0; x < 10; i++) {
System.out.println("Still Looping");
}

weil (x) niemals den Wert 10 erreichen würde;

Sie können auch eine Endlosschleife mit einer doppelten for-Schleife erstellen:

int i ;

  for (i = 0; i <= 10; i++) {
      for (i = 0; i <= 5; i++){
         System.out.println("Repeat");   
      }
 }

Diese Schleife ist unendlich, weil die erste for-Schleife i < 10 sagt, was wahr ist, also geht sie in die zweite for-Schleife und die zweite for-Schleife erhöht den Wert von (i), bis er == 5 ist. Dann geht es weiter in die erste For-Schleife erneut, da i < 10, wiederholt sich der Prozess ständig, da er nach der zweiten for-Schleife zurückgesetzt wird

Es ist eine endliche Schleife, weil einmal der Wert von x übersteigt 2,147,483,647 (das ist der maximale Wert von an int), x wird negativ und nicht größer als j nicht mehr, ob Sie y drucken oder nicht.

Sie können einfach den Wert von ändern y zu 100000 und drucken y in der Schleife und die Schleife wird sehr bald brechen.

Der Grund, warum Sie das Gefühl haben, dass es unendlich wurde, ist, dass die System.out.println(y); machte den auszuführenden Code sehr viel langsamer als ohne Aktionen.

Interessantes Problem Tatsächlich ist die Schleife in beiden Fällen nicht endlos

Aber der Hauptunterschied zwischen ihnen ist, wann es endet und wie lange es dauert x wird dauern, um max zu überschreiten int Wert, der ist 2,147,483,647 Danach erreicht es den Überlaufzustand und die Schleife wird beendet.

Der beste Weg, dieses Problem zu verstehen, besteht darin, ein einfaches Beispiel zu testen und die Ergebnisse zu erhalten.

Beispiel:

for(int i = 10; i > 0; i++) {}
System.out.println("finished!");

Ausgabe:

finished!
BUILD SUCCESSFUL (total time: 0 seconds)

Nach dem Testen dieser Endlosschleife dauert es weniger als 1 Sekunde, bis sie beendet ist.

for(int i = 10; i > 0; i++) {
    System.out.println("infinite: " + i);
}
System.out.println("finished!");

Ausgabe:

infinite: 314572809
infinite: 314572810
infinite: 314572811
.
.
.
infinite: 2147483644
infinite: 2147483645
infinite: 2147483646
infinite: 2147483647
finished!
BUILD SUCCESSFUL (total time: 486 minutes 25 seconds)

Bei diesem Testfall werden Sie einen großen Unterschied in der Zeit feststellen, die zum Beenden und Ausführen des Programms benötigt wird.

Wenn Sie nicht geduldig sind, werden Sie denken, dass diese Schleife endlos ist und nicht beendet wird, aber tatsächlich wird es Stunden dauern, bis sie beendet ist und den Überlaufzustand erreicht i Wert.

Schließlich kamen wir zu dem Schluss, nachdem wir die print-Anweisung in die for-Schleife eingefügt hatten, dass es im ersten Fall ohne print-Anweisung viel mehr Zeit in Anspruch nehmen würde als die Schleife.

Die Zeit, die zum Ausführen des Programms benötigt wird, hängt von Ihren Computerspezifikationen ab, insbesondere von der Verarbeitungsleistung (Prozessorkapazität), dem Betriebssystem und Ihrer IDE, die das Programm kompiliert.

Ich teste diesen Fall auf:

Lenovo 2,7 GHz Intel Core i5

Betriebssystem: Windows 8.1 64x

IDE: NetBeans 8.2

Es dauert ungefähr 8 Stunden (486 Minuten), um das Programm zu beenden.

Sie können auch feststellen, dass der Schritt in der for-Schleife erhöht wird i = i + 1 ist ein sehr langsamer Faktor, um den maximalen int-Wert zu erreichen.

Wir können diesen Faktor ändern und das Schrittinkrement schneller machen, um die Schleife in kürzerer Zeit zu testen.

wenn wir setzen i = i * 10 und teste es:

for(int i = 10; i > 0; i*=10) {
           System.out.println("infinite: " + i);
}
     System.out.println("finished!");

Ausgabe:

infinite: 100000
infinite: 1000000
infinite: 10000000
infinite: 100000000
infinite: 1000000000
infinite: 1410065408
infinite: 1215752192
finished!
BUILD SUCCESSFUL (total time: 0 seconds)

Wie Sie sehen, ist es im Vergleich zur vorherigen Schleife sehr schnell

Es dauert weniger als 1 Sekunde, um das Programm zu beenden und zu beenden.

Nach diesem Testbeispiel sollte es meiner Meinung nach das Problem verdeutlichen und die Gültigkeit der Antwort von Zbynek Vyskovsky – kvr000 beweisen. Außerdem wird es eine Antwort auf diese Frage sein.

1268200cookie-checkEine scheinbar endlose Schleife wird beendet, es sei denn, System.out.println wird verwendet

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy