web-dev-qa-db-fra.com

Une boucle apparemment sans fin se termine, sauf si System.out.println est utilisé

J'avais un simple morceau de code qui était supposé être une boucle sans fin, car x sera toujours en croissance et restera toujours plus gros que j

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

mais tel quel, il imprime y et ne boucle pas sans fin. Je ne peux pas comprendre pourquoi. Cependant, lorsque j'ajuste le code de la manière suivante:

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

Cela devient une boucle sans fin et je ne sais pas pourquoi. Est-ce que Java reconnaît sa boucle sans fin et la saute dans la première situation mais doit exécuter un appel de méthode dans la seconde pour qu'elle se comporte comme prévu? Confused :)

85
Omar

Les deux exemples ne sont pas sans fin.

Le problème est la limitation du type int en Java (ou à peu près tout autre langage commun). Lorsque la valeur de x atteint 0x7fffffff, l'ajout de toute valeur positive entraînera un dépassement de capacité et la variable x devient négative, donc inférieure à j.

La différence entre la première et la deuxième boucle est que le code interne prend beaucoup plus de temps et qu'il faudrait probablement plusieurs minutes pour que x déborde. Pour le premier exemple, cela peut prendre moins d'une seconde ou très probablement, le code sera supprimé par l'optimiseur car il n'a aucun effet.

Comme mentionné dans la discussion, le temps dépendra énormément de la manière dont le système d’exploitation tamponne la sortie, qu’il soit envoyé à l’émulateur de terminal, etc.

159

Comme ils sont déclarés en tant qu'int, une fois que la valeur maximale est atteinte, la boucle se rompt lorsque la valeur x devient négative. 

Cependant, lorsque System.out.println est ajouté à la boucle, la vitesse d'exécution devient visible (la sortie sur la console ralentissant la vitesse d'exécution). Cependant, si vous laissez le second programme (celui avec syso dans la boucle) fonctionner assez longtemps, il devrait avoir le même comportement que le premier (celui sans syso dans la boucle). 

33
Ace Zachary

Il peut y avoir deux raisons à cela:

  1. Java optimise la boucle for et puisqu'il n'y a aucune utilisation de x après la boucle, supprime simplement la boucle. Vous pouvez vérifier cela en mettant l'instruction System.out.println(x); après la boucle.

  2. Il est possible que Java n’optimise pas réellement la boucle, qu’il exécute le programme correctement et que x finisse par devenir trop volumineux pour int et le débordement. Un dépassement d'entier rendra probablement le nombre entier x comme négatif, qui sera inférieur à j. Il sortira donc de la boucle et affichera la valeur de y. Ceci peut également être vérifié en ajoutant System.out.println(x); après la boucle.

En outre, même dans le premier cas, un débordement finira par se produire, ce qui le rendra dans le deuxième cas, de sorte qu'il ne s'agira jamais d'une véritable boucle sans fin.

13
Ashok Vishnoi

C'est une boucle finie car une fois que la valeur de x dépasse 2,147,483,647 (qui est la valeur maximale d'un int), x deviendra négatif et ne sera plus supérieur à j, que vous imprimiez y ou non.

Vous pouvez simplement changer la valeur de y en 100000 et imprimer y dans la boucle et celle-ci se cassera très bientôt.

La raison pour laquelle vous pensez que cela est devenu infini est que la fonction System.out.println(y); a rendu le code exécuté beaucoup plus lentement que sans action.

1
Joe Cheng

Ce ne sont pas des boucles sans fin, Initialement j = 0, tant que j <x, j augmente (j ++), et j est un entier De sorte que la boucle s'exécute jusqu'à atteindre la valeur maximale puis déborde ( Un dépassement d'entier est la condition qui survient lorsque le résultat d'une opération arithmétique, telle que la multiplication ou l'addition, dépasse la taille maximale du type entier utilisé pour le stocker.) . Pour le deuxième exemple, le système imprime simplement la valeur de y jusqu'à la rupture de la boucle.

si vous cherchez un exemple de boucle sans fin, devrait ressembler à ceci

int x = 6;

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

parce que (x) n’atteindrait jamais la valeur de 10;

vous pouvez également créer une boucle infinie avec une double boucle for:

int i ;

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

cette boucle est infinie car la première boucle for dit i <10, Ce qui est vrai, elle passe donc à la seconde pour la boucle et la seconde à la boucle augmente la valeur de (i) jusqu'à ce qu'elle soit == 5. Ensuite, il passe à nouveau dans la première boucle for .__ parce que i <10, le processus continue de se répéter car il se réinitialise après la seconde boucle for.

1
Kennedy

Problème intéressant En réalité, dans les deux cas, la boucle n'est pas sans fin

Mais la principale différence entre eux réside dans le moment où il se terminera et le temps nécessaire à x pour dépasser la valeur maximale de int qui correspond à 2,147,483,647 après le moment où il atteindra l'état de dépassement et la boucle se terminera. 

Le meilleur moyen de comprendre ce problème est de tester un exemple simple et de préserver ses résultats.

Exemple:

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

Sortie:

finished!
BUILD SUCCESSFUL (total time: 0 seconds)

Après avoir testé cette boucle infinie, il faudra moins de 1 seconde pour terminer.

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

Sortie:

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

Sur ce cas de test, vous remarquerez une énorme différence dans le temps pris pour terminer et exécuter le programme.

Si vous n'êtes pas patient, vous penserez que cette boucle est sans fin et ne se terminera pas, mais en réalité, il faudra des heures pour terminer et atteindre l'état de dépassement de capacité à la valeur i

Enfin, nous avons conclu après avoir placé l'instruction print dans la boucle qu'il faudra beaucoup plus de temps que la boucle dans le premier cas sans instruction print.

Le temps nécessaire à l’exécution du programme dépend des spécifications de votre ordinateur, notamment de la puissance de traitement (capacité du processeur), du système d’exploitation et de votre IDE compilant le programme.

Je teste ce cas sur:

Lenovo 2.7 GHz Intel Core i5

OS: Windows 8.1 64x

IDE: NetBeans 8.2

Il faut environ 8 heures (486 minutes) pour terminer le programme.

Vous pouvez également remarquer que l’incrémentation dans la boucle for i = i + 1 est un facteur très lent pour atteindre la valeur maximale de int. 

Nous pouvons modifier ce facteur et accélérer l’incrémentation afin de tester la boucle en moins de temps.

si nous mettons i = i * 10 et le testons:

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

Sortie:

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

Comme vous le voyez, la comparaison avec la boucle précédente est très rapide

il faut moins de 1 seconde pour terminer et exécuter le programme.

Après cet exemple de test, je pense que cela devrait clarifier le problème et prouver la validité de Zbynek Vyskovsky - La réponse de kvr000 , ce sera également une réponse à cette question

0
Oghli