C ++ alors que l’optimisation de la boucle ne fonctionne pas correctement

J’ai ce segment de code:

#include  int main(int argc, const char** argv) { int a = argv[0][0]; int b = argv[0][1]; while ((a >= 0) && (a < b)) { printf("a = %d\n", a); a++; } return 0; } 

et je le comstack avec gcc-4.5 -02 -Wssortingct-overflow=5 .

Le compilateur me lance un warning: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C1 +- C2

Qu’est-ce que cela signifie exactement?

Si j’ai raison, cette boucle ne provoquera jamais de débordement, car pour être incrémentée, elle doit être inférieure à un autre entier. Si elle est plus grande, la boucle est terminée.

Quelqu’un peut-il m’expliquer ce comportement?

La norme C ++ dit que si un calcul d’entier signé produit un résultat en dehors de la plage représentable pour le type, le comportement est indéfini. Le dépassement d’entier est UB. Une fois qu’UB est arrivé, la mise en œuvre est libre de faire ce qu’elle veut.

De nombreux compilateurs appliquent des optimisations en partant de l’hypothèse explicite qu’UB ne se produit pas. [Ou si c’est le cas, le code pourrait être faux mais c’est votre problème!]

Ce compilateur vous informe qu’il applique une telle optimisation à un calcul où il est impossible de déterminer à partir de l’parsing du code que UB ne se produit pas.

Vos choix en général sont:

  1. Assurez-vous qu’UB ne peut pas se produire et ignorez l’avertissement.
  2. Permettez à UB de se produire et vivez avec les conséquences.
  3. Réécrivez le code afin que l’UB ne puisse vraiment pas arriver et que le compilateur sache que cela ne peut pas arriver, et l’avertissement devrait disparaître.

Je recommanderais la dernière option. Des tests de scope simples sur a et b devraient suffire.


Mon hypothèse est que le compilateur émet cette erreur car la boucle traite des valeurs complètement inconnues et ne parvient pas à parsingr le stream de données suffisamment bien pour déterminer si la récurrence de périphérique peut se produire ou non.

Grâce à notre pouvoir de raisonnement supérieur, nous pouvons nous convaincre que l’UB ne peut pas se produire, nous pouvons donc ignorer l’erreur. En fait, une lecture attentive du message d’erreur peut nous laisser demander s’il est pertinent ou non. Où sont ces deux valeurs constantes C1 et C2 ?

Nous pourrions aussi noter qu’un ne peut jamais être négatif, alors pourquoi ce test est-il dans la boucle? Je réécrirais probablement le code pour supprimer l’erreur (mais par expérience, cela peut être un exercice auto-destructeur). Essayez ceci et voyez ce qui se passe (et évitez les encombrements inutiles entre parenthèses):

 if (a >= 0) { while (a < b) { ... ++a; } } 

Le compilateur procède à une optimisation pour convertir a + 1 < b en a < b - 1 . Cependant, si b est INT_MIN cela entraînera un débordement, ce qui constitue un changement de comportement. C'est ce dont il avertit.

Bien sûr, vous pouvez probablement dire que cela est impossible, mais le compilateur dispose de ressources limitées pour résoudre le problème et ne procède généralement pas à une parsing approfondie des chemins de données.

Ajouter une vérification que b >= 0 peut résoudre le problème.

Edit : Une autre possibilité est de déplacer a >= 0 en dehors de la boucle, car (en supposant qu'il n'y ait pas de dépassement de capacité), cela ne peut jamais changer. Là encore, l'hypothèse peut ne pas être valable pour toutes les entrées (c'est-à-dire si b est négatif). Vous auriez besoin de vérifier l'assemblage final pour voir ce qu'il a réellement fait.

Ce que le compilateur vous avertit, c’est qu’il suppose que le débordement signé ne se produit pas dans le code d’origine.

L’avertissement ne signifie pas “je suis sur le point d’écrire une optimisation qui pourrait entraîner des débordements”.

En d’autres termes, si votre programme dépend du débordement (c’est-à-dire qu’il n’est pas très portable), l’optimisation du compilateur peut en modifier le comportement. (Veuillez donc vérifier par vous-même que ce code ne dépend pas du débordement).

Par exemple, si vous avez “a + b> c” et que vous dépendez de l’échec de ce test lorsque l’arithmétique a + b est bouclée (comportement typique du complément à deux), alors si cela se transforme algébriquement en “a> c – b “, alors cela peut réussir, car c – b peut ne pas déborder et produire une valeur inférieure à a.

Remarques: Seuls les programmes C peuvent invoquer un comportement indéfini. Lorsque les compilateurs sont incorrects, ils sont “non conformes”. Un compilateur ne peut être non conforme (à la norme C) que s’il fait erreur avec un code conforme à la norme (C standard). Une optimisation qui modifie le comportement correct et portable est une optimisation non conforme.