web-dev-qa-db-fra.com

Programme se comportant étrangement sur les IDE en ligne

J'ai rencontré le programme C++ ci-dessous ( source ):

#include <iostream>
int main()
{
    for (int i = 0; i < 300; i++)
        std::cout << i << " " << i * 12345678 << std::endl;
}

Cela ressemble à un programme simple et donne la sortie correcte sur ma machine locale, à savoir quelque chose comme:

0 0
1 12345678
2 24691356
...
297 -628300930
298 -615955252
299 -603609574

Mais, sur des IDE en ligne comme codechef , cela donne le résultat suivant:

0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597

Pourquoi la boucle for ne se termine-t-elle pas à 300? De plus, ce programme se termine toujours le 4169. Pourquoi 4169 et pas une autre valeur?

77
arpanmangal

Je vais supposer que les compilateurs en ligne utilisent GCC ou un compilateur compatible. Bien entendu, tout autre compilateur est également autorisé à effectuer la même optimisation, mais la documentation de GCC explique bien ce qu'elle fait:

-faggressive-loop-optimizations

Cette option indique à l'optimiseur de boucle d'utiliser des contraintes de langage pour dériver des bornes pour le nombre d'itérations d'une boucle. Cela suppose que le code de la boucle n'invoque pas de comportement indéfini, par exemple en provoquant des débordements d'entiers signés ou des accès à des tableaux hors limites. Les limites du nombre d'itérations d'une boucle sont utilisées pour guider le déroulement et l'optimisation des tests de pelage et de sortie de boucle. Cette option est activée par défaut.

Cette option permet simplement de faire des hypothèses sur des cas où UB est prouvé. Pour tirer parti de ces hypothèses, il peut être nécessaire d'activer d'autres optimisations, telles que le repliement constant.


Le dépassement d'entier signé a un comportement indéfini. L'optimiseur a été en mesure de prouver que toute valeur de i supérieure à 173 causerait un problème d'UB, et comme il peut supposer qu'il n'y a pas d'UB, il peut également supposer que i n'est jamais supérieur à 173. Il peut alors prouver que i < 300 est toujours vrai et la condition de boucle peut ainsi être optimisée.

Pourquoi 4169 et pas une autre valeur?

Ces sites limitent probablement le nombre de lignes en sortie (ou caractères ou octets) qu’ils affichent et partagent la même limite.

102
eerorika

"Le comportement indéfini n'est pas défini." (c)

Un compilateur utilisé sur codechef semble utiliser la logique suivante:

  1. Un comportement indéfini ne peut pas arriver.
  2. i * 12345678 déborde et aboutit à UB si i > 173 (en supposant que 32 bits ints).
  3. Ainsi, i ne peut jamais dépasser 173.
  4. Ainsi i < 300 est superflu et peut être remplacé par true.

La boucle elle-même semble être infinie. Apparemment, codechef arrête simplement le programme après un laps de temps spécifique ou tronque la sortie.

34
HolyBlackCat

Vous appelez comportement indéfini probablement à la 174ème itération de votre boucle for car la valeur maximale de int est probablement 2147483647 encore 174 * 123456789 expression évaluée à 2148147972 qui est un comportement non défini car il n'y a pas de dépassement d'entier signé. Vous observez donc les effets de l’UB, en particulier avec le compilateur GCC avec des indicateurs d’optimisation définis dans votre cas. Il est probable que le compilateur vous l'aurait averti en émettant l'avertissement suivant:

warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]

Retirer le (-O2) indicateurs d'optimisation pour observer différents résultats.

8
Ron

Le compilateur peut supposer que le comportement non défini ne se produira pas et que, comme le débordement signé est UB, il peut supposer que jamais i * 12345678 > INT_MAX, donc aussi i <= INT_MAX / 12345678 < 300 et donc décocher le chèque i < 300.

6
yassin