74 votes

La compilation C++ bug?

J'ai le code suivant:

#include <iostream>
#include <complex>
using namespace std;

int main() {
    complex<int> delta;
    complex<int> mc[4] = {0};

    for(int di = 0; di < 4; di++, delta = mc[di]) {
        cout << di << endl;
    }

    return 0;
}

Je l'attends à la sortie "0, 1, 2, 3" et d'arrêter, mais il produit une série infinie de "0, 1, 2, 3, 4, 5, ....."

Il ressemble à la comparaison di<4 ne fonctionne pas bien, et retourne toujours true.

Si j'ai juste un commentaire ,delta=mc[di], j'obtiens un "0, 1, 2, 3" que la normale. Quel est le problème avec les innocents affectation?

Je suis à l'aide de Ideone.com g++ C++14 avec -O2 option.

109voto

Shafik Yaghmour Points 42198

Cela est probablement dû à un comportement indéfini, vous accédez au tableau mc hors limites sur la dernière itération de la boucle. Certains compilateurs peuvent effectuer agressif optimisation en boucle autour des hypothèses de pas un comportement indéfini. La logique serait semblable à la suivante:

  • Accéder mc en dehors des limites est un comportement indéfini
  • Supposons pas d'un comportement indéfini
  • Par conséquent, di < 4 est toujours vrai car, autrement, mc[di] invoque un comportement indéfini

gcc avec l'optimisation de tournée et à l'aide de l' -fno-aggressive-loop-optimizations indicateur provoque la boucle infinie comportement à disparaître(voir en direct). Tandis qu'un exemple vivant avec l'optimisation, mais sans l'option-fno-agressif-boucle-optimisations des expositions de la boucle infinie comportement à observer.

Afin d'obtenir un avertissement de gcc, nous devons aller de l' cout en dehors de la boucle et puis, nous voyons l'avertissement suivant (voir en direct):

warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
     for(di=0; di<4;di++,delta=mc[di]){ }
     ^

ce qui aurait sans doute été suffisant pour fournir les OP avec assez d'informations pour comprendre ce qui se passait. L'incohérence de ce genre sont typiques des types de comportement que nous pouvons le voir avec un comportement indéfini.

Un godbolt live exemple de code montre l' di < 4 vérification est supprimée et remplacée par et inconditionnel jmp:

jmp .L6

C'est presque identique au cas décrit dans GCC pré-4.8 Pauses Cassé SPEC 2006 Repères. Les commentaires de cet article est excellent et vaut bien le lire. Il note que clang attrapé le cas dans l'article à l'aide d' -fsanitize=undefined que je ne peux pas reproduire, pour ce cas, mais à l'aide de gcc -fsanitize=undefined (voir en direct).

Bien que ce soit un des optimisations agressives, il est important de noter que la norme C++, dit un comportement indéfini est:

le comportement pour lequel la présente Norme Internationale n'impose pas d'exigences

Essentiellement, cela signifie que tout est possible et il notes (c'est moi qui souligne):

[...]Admissible comportement indéfini les plages d'ignorer la situation complètement avec des résultats imprévisibles, à se comporter lors de la traduction l'exécution du programme dans documenté de façon caractéristique de l'environnement (avec ou sans émission de un message de diagnostic), à la terminaison d'une traduction ou d'exécution (avec l'émission d'un message de diagnostic).[...]

Remarque, -fno-aggressive-loop-optimizations est documenté dans le gcc 4.8 notes de version.

38voto

Logicrat Points 1016

Puisque vous êtes l'incrémentation di avant de l'utiliser pour indexer mc, pour la quatrième fois à travers la boucle de votre sera de référencement de mc[4], qui est passé à la fin de votre tableau, ce qui pourrait à son tour conduire les comportements difficiles.

5voto

Francis Cugler Points 480

Vous avez ceci:

for(int di=0; di<4; di++, delta=mc[di]) {
  cout<<di<<endl;
}

Essayez plutôt ceci:

for(int di=0; di<4; delta=mc[di++]) {
   cout<<di<<endl;
}

EDIT:

Afin de clarifier ce qui se passe Permet de Briser L'Itération De Ta Boucle For:

1ère itération: Initialement di est mis à 0. Comparaison vérifier: Est di moins de 4? Oui d'accord de procéder. Incrément de di par 1. Maintenant, di = 1. Saisir la "nième" élément de mc[] et il faut le delta. Cette fois, nous sommes en saisissant le 2ème élément, puisque cette valeur indexée de 1 et non de 0. Enfin effectuer le bloc de code/s à l'intérieur de la boucle for.

2ème itération: Maintenant, di est fixé à 1. Comparaison vérifier: Est di moins de 4? Oui et suivez les instructions. Incrément de di par 1. Maintenant, di = 2. Saisir la "nième" élément de mc[] et il faut le delta. Cette fois, nous emparer de la 3ème élément, puisque cette valeur indexée est 2. Enfin effectuer le bloc de code/s à l'intérieur de la boucle for.

3ème itération: Maintenant, di est fixé à 2. Comparaison vérifier: Est di moins de 4? Oui et suivez les instructions. Incrément de di par 1. Maintenant, di = 3. Saisir la "nième" élément de mc[] et il faut le delta. Cette fois, nous emparer de la 4ème élément, puisque cette valeur indexée est 3. Enfin effectuer le bloc de code/s à l'intérieur de la boucle for.

4ème itération: Maintenant, di est fixé à 3. Comparaison vérifier: Est di moins de 4? Oui et suivez les instructions. Incrément de di par 1. Maintenant, di = 4. (Pouvez-vous voir où cela va?) Saisir la "nième" élément de mc[] et il faut le delta. Cette fois, nous sommes en saisissant le 5ème élément, puisque cette valeur indexée est 4. Uh Oh, nous avons un problème; notre tableau de taille n'est que de 4. Delta a maintenant des ordures et c'est un comportement indéfini ou de corruption. Enfin effectuer le bloc de code/s à l'intérieur de la boucle à l'aide de "poubelle delta".

5ème itération. Maintenant, di est fixé à 4. Comparaison vérifier: Est di moins de 4? Non, sortir de la boucle.

La Corruption par un dépassement des limites de mémoire contiguë (tableau).

5voto

Paul Herbert Points 31

C'est parce que di++ est exécutée lors de la dernière exécution de la boucle.

Par exemple;

int di = 0;
for(; di < 4; di++);
// after the loop di == 4
// (inside the loop we see 0,1,2,3)
// (inside the for statement, after di++, we see 1,2,3,4)

Vous accédez à mc[] quand di == 4, donc c'est un en dehors des limites de problème, potentiellement détruit une partie de la pile et de la corruption de la variable di.

une solution serait:

for(int di = 0; di < 4; di++) {
    cout << di << endl;
    delta = mc[di];
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X