Il ciclo apparentemente infinito termina, a meno che non venga utilizzato System.out.println


91

Avevo un semplice bit di codice che avrebbe dovuto essere un ciclo infinito poiché xsarà sempre in crescita e rimarrà sempre più grande di j.

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

ma così com'è, stampa ye non si ripete all'infinito. Non riesco a capire perché. Tuttavia, quando modifico il codice nel modo seguente:

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

Diventa un loop infinito e non ho idea del perché. Java riconosce che è un ciclo infinito e lo salta nella prima situazione, ma deve eseguire una chiamata al metodo nella seconda in modo che si comporti come previsto? Confuso :)


4
Il secondo ciclo è infinito perché il limite superiore xcresce più velocemente della variabile del ciclo j. In altre parole, jnon raggiungerà mai un limite superiore, quindi il ciclo verrà eseguito "per sempre". Beh, non per sempre, molto probabilmente avrai un overflow ad un certo punto.
Tim Biegeleisen

75
Non è un ciclo infinito, è solo che ci vogliono 238609294 volte per uscire dal ciclo for nel primo caso e la seconda volta stampa il valore di y238609294 volte
N00b Pr0grammer

13
risposta con una sola parola: overflow
qwr

20
In modo divertente, System.out.println(x)invece che yalla fine avrebbe mostrato immediatamente qual era il problema
JollyJoker

9
@TeroLahtinen no, non lo sarebbe. Leggi le specifiche del linguaggio Java se hai dubbi su cosa sia il tipo int. È indipendente dall'hardware.
9ilsdx 9rvj 0lo

Risposte:


161

Entrambi gli esempi non sono infiniti.

Il problema è la limitazione del inttipo in Java (o praticamente in qualsiasi altro linguaggio comune). Quando il valore di xraggiunge 0x7fffffff, l'aggiunta di un qualsiasi valore positivo comporterà un overflow e xdiventerà negativo, quindi inferiore a j.

La differenza tra il primo e il secondo ciclo è che il codice interno richiede molto più tempo e probabilmente ci vorranno diversi minuti prima che xesca. Per il primo esempio potrebbe volerci meno del secondo o molto probabilmente il codice verrà rimosso dall'ottimizzatore poiché non ha alcun effetto.

Come accennato nella discussione, il tempo dipenderà fortemente da come il sistema operativo bufferizza l'output, se viene inviato all'emulatore di terminale ecc., Quindi può essere molto più alto di pochi minuti.


48
Ho appena provato un programma (sul mio laptop) che stampa una riga in un ciclo. L'ho cronometrato ed è stato in grado di stampare circa 1000 righe / secondo. Sulla base del commento di N00b secondo cui il ciclo verrà eseguito 238609294 volte, ci vorranno circa 23861 secondi per terminare il ciclo - oltre 6,6 ore. Un po 'più di "diversi minuti".
ajb

11
@ajb: dipende dall'implementazione. IIRC println()su Windows è un'operazione di blocco, mentre su (alcuni?) Unix è bufferizzato, quindi è molto più veloce. Prova anche a usare print(), che buffer fino a quando non colpisce un \n(o il buffer si riempie, o flush()viene chiamato)
BlueRaja - Danny Pflughoeft

6
Inoltre dipende dal terminale che mostra l'output. Vedi stackoverflow.com/a/21947627/53897 per un esempio estremo (dove il rallentamento era dovuto al ritorno a capo automatico)
Thorbjørn Ravn Andersen

1
Sì, è bufferizzato su UNIX, ma sta ancora bloccando. Una volta che il buffer 8K circa si riempie, si bloccherà finché non c'è spazio. La velocità dipenderà fortemente dalla velocità con cui viene consumato. Il reindirizzamento dell'output a / dev / null sarà il più veloce, ma averlo inviato al terminale, l'impostazione predefinita, richiederà aggiornamenti grafici allo schermo e molta più potenza di calcolo in quanto rende i caratteri rallentandoli.
penguin359

2
@Zbynek oh, probabilmente sì, ma questo mi ricorda, i terminali I / O saranno tipicamente bufferizzati di linea, non bloccati, quindi molto probabilmente ogni println risulterà in una chiamata di sistema che rallenta ulteriormente il case del terminale.
penguin359

33

Poiché sono dichiarati come int, una volta raggiunto il valore massimo, il ciclo si interromperà poiché il valore x diventerà negativo.

Ma quando System.out.println viene aggiunto al ciclo, la velocità di esecuzione diventa visibile (poiché l'output su console rallenta la velocità di esecuzione). Tuttavia, se lasci che il secondo programma (quello con syso all'interno del ciclo) funzioni abbastanza a lungo, dovrebbe avere lo stesso comportamento del primo (quello senza syso all'interno del ciclo).


21
Le persone non si rendono conto di quanto lo spamming sulla console possa rallentare il loro codice.
user9993

13

Ci possono essere due ragioni per questo:

  1. Java ottimizza il forciclo e poiché non è utilizzato xdopo il ciclo, rimuove semplicemente il ciclo. Puoi verificarlo inserendo l' System.out.println(x);istruzione dopo il ciclo.

  2. Potrebbe essere possibile che Java non stia effettivamente ottimizzando il ciclo e che stia eseguendo il programma correttamente e alla fine xdiventerà troppo grande inte overflow. L'overflow di un numero intero molto probabilmente renderà il numero intero xnegativo che sarà più piccolo di j e quindi uscirà dal ciclo e stamperà il valore di y. Questo può anche essere verificato aggiungendo System.out.println(x);dopo il ciclo.

Inoltre, anche nel primo caso alla fine si verificherà un overflow, rendendolo così nel secondo caso, quindi non sarà mai un vero ciclo infinito.


14
Scelgo la porta numero 2.
Robby Cornelissen

Vero. È andato sulla scala negativa ed è uscito dal ciclo. Ma sysoutè così lento da aggiungere l'illusione di un ciclo infinito.
Pavan Kumar

4
1. Sarebbe un bug. Le ottimizzazioni del compilatore non possono modificare il comportamento di un programma. Se questo fosse un ciclo infinito, il compilatore potrebbe ottimizzare tutto ciò che vuole, tuttavia, il risultato deve comunque essere un ciclo infinito. La vera soluzione è che l'OP è sbagliato: nessuno dei due è un ciclo infinito, uno fa solo più lavoro dell'altro, quindi ci vuole più tempo.
Jörg W Mittag

1
@ JörgWMittag In questo caso x è una variabile locale senza alcuna relazione con nient'altro. In quanto tale, è possibile che sia ottimizzato. Ma si dovrebbe guardare al bytecode per determinare se è così, non dare mai per scontato che il compilatore abbia fatto qualcosa del genere.
spera utile il

1

Non sono entrambi cicli infiniti, inizialmente j = 0, fintanto che j <x, j aumenta (j ++) e j è un numero intero, quindi il ciclo verrà eseguito fino a raggiungere il valore massimo, quindi overflow (An Integer Overflow è la condizione che si verifica quando il risultato di un'operazione aritmetica, come la moltiplicazione o l'addizione, supera la dimensione massima del tipo intero utilizzato per memorizzarlo.). per il secondo esempio il sistema stampa semplicemente il valore di y finché il ciclo non si interrompe.

se stai cercando un esempio di un ciclo infinito, dovrebbe assomigliare a questo

int x = 6;

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

perché (x) non raggiungerebbe mai il valore di 10;

potresti anche creare un ciclo infinito con un doppio ciclo for:

int i ;

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

questo ciclo è infinito perché il primo ciclo for dice i <10, il che è vero quindi va nel secondo ciclo for e il secondo ciclo for aumenta il valore di (i) fino a quando diventa == 5. Quindi procede nel primo di nuovo il ciclo for perché i <10, il processo continua a ripetersi perché si ripristina dopo il secondo ciclo for


1

È un ciclo finito perché una volta che il valore di xsupera 2,147,483,647(che è il valore massimo di an int), xdiventerà negativo e non maggiore di jqualsiasi altro, indipendentemente dal fatto che si stampi y o meno.

Puoi semplicemente cambiare il valore di ya 100000e stampare ynel ciclo e il ciclo si interromperà molto presto.

Il motivo per cui ritieni che sia diventato infinito è che ha System.out.println(y);reso il codice da eseguire molto più lentamente che senza alcuna azione.


0

Problema interessante In realtà in entrambi i casi il ciclo non è infinito

Ma la differenza principale tra loro è quando terminerà e quanto tempo ximpiegherà per superare il intvalore massimo , 2,147,483,647dopodiché raggiungerà lo stato di overflow e il ciclo terminerà.

Il modo migliore per comprendere questo problema è provare un semplice esempio e preservarne i risultati.

Esempio :

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

Produzione:

finished!
BUILD SUCCESSFUL (total time: 0 seconds)

Dopo aver testato questo ciclo infinito, ci vorrà meno di 1 secondo per terminare.

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

Produzione:

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

In questo caso di prova noterai un'enorme differenza nel tempo impiegato per terminare e completare l'esecuzione del programma.

Se non hai pazienza, penserai che questo ciclo sia infinito e non terminerà, ma in realtà ci vorranno ore per terminare e raggiungere lo stato di overflow al ivalore.

Alla fine abbiamo concluso dopo aver inserito l'istruzione print all'interno del ciclo for che ci vorrà molto più tempo rispetto al ciclo nel primo caso senza l'istruzione print.

Il tempo impiegato per eseguire il programma dipende dalle specifiche del computer, in particolare dalla potenza di elaborazione (capacità del processore), dal sistema operativo e dall'IDE che sta compilando il programma.

Provo questo caso su:

Lenovo 2,7 GHz Intel Core i5

Sistema operativo: Windows 8.1 64x

IDE: NetBeans 8.2

Sono necessarie circa 8 ore (486 minuti) per completare il programma.

Inoltre puoi notare che l'incremento del passo nel ciclo for i = i + 1è un fattore molto lento per raggiungere il valore massimo int.

Possiamo cambiare questo fattore e rendere l'incremento del passo più veloce per testare il loop in meno tempo.

se lo mettiamo i = i * 10e lo testiamo:

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

Produzione:

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

Come vedi è molto veloce rispetto al ciclo precedente

ci vuole meno di 1 secondo per terminare e terminare l'esecuzione del programma.

Dopo questo esempio di test penso che dovrebbe chiarire il problema e dimostrare la validità di Zbynek Vyskovsky - la risposta di kvr000 , anche questa sarà la risposta a questa domanda .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.