Bogue de compilation C ++?

J’ai le code suivant:

#include  #include  using namespace std; int main() { complex delta; complex mc[4] = {0}; for(int di = 0; di < 4; di++, delta = mc[di]) { cout << di << endl; } return 0; } 

Je m’attends à ce qu’il affiche “0, 1, 2, 3” et arrête, mais il produit une série sans fin de “0, 1, 2, 3, 4, 5, …..”

Il semble que la comparaison di<4 ne fonctionne pas bien et renvoie toujours la valeur true.

Si je ne fais que commenter ,delta=mc[di] , j’obtiens “0, 1, 2, 3” comme d’habitude. Quel est le problème avec la mission innocente?

J’utilise Ideone.com g ++ C ++ 14 avec l’option -O2.

    Cela est dû à un comportement non défini, vous accédez au tableau mc hors limites lors de la dernière itération de votre boucle. Certains compilateurs peuvent effectuer une optimisation agressive de la boucle autour des hypothèses de comportement non défini. La logique serait similaire à la suivante:

    • L’access à mc hors limites est un comportement indéfini
    • Ne supposez aucun comportement indéfini
    • Donc di < 4 est toujours vrai car sinon mc[di] invoquerait un comportement indéfini

    gcc avec l'optimisation activée et l'utilisation de l' -fno-aggressive-loop-optimizations entraînent la disparition du comportement de la boucle infinie ( voir la fonction live ). Alors qu'un exemple réel avec optimisation mais sans optimisations -fno-agressif-boucle- montre le comportement de boucle infinie que vous observez.

    Un exemple concret du code montre que le contrôle di < 4 est supprimé et remplacé par jmp inconditionnel:

     jmp .L6 

    Ceci est presque identique au cas décrit dans les points de référence de GCC antérieurs à 4.8 . Les commentaires sur cet article sont excellents et valent bien la lecture. Il note que clang a saisi le cas dans l'article en utilisant -fsanitize=undefined que je ne peux pas reproduire pour ce cas mais que gcc en utilisant -fsanitize=undefined fait ( voir le live ). Probablement le bug le plus sortingstement célèbre autour d'un optimiseur faisant une inférence sur le comportement non défini est la suppression de vérification du pointeur null du kernel Linux .

    Bien qu'il s'agisse d'une optimisation agressive, il est important de noter que, comme le dit le standard C ++, le comportement non défini est le suivant:

    comportement pour lequel la présente Norme internationale n'impose aucune exigence

    Ce qui signifie essentiellement que tout est possible et il note ( emphase le mien ):

    [...] Le comportement indéfini admissible va de l'ignorance complète de la situation à des résultats imprévisibles , au comportement lors de la traduction ou à l'exécution du programme d'une manière documentée caractéristique de l'environnement (avec ou sans message de diagnostic), exécution (avec émission d'un message de diagnostic). [...]

    Pour obtenir un avertissement de gcc, nous devons déplacer le cout dehors de la boucle, 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 probablement suffi à fournir au PO suffisamment d’informations pour comprendre ce qui se passait. Une telle incohérence est typique des types de comportement que nous pouvons voir avec un comportement indéfini. Pour mieux comprendre pourquoi une telle mise en garde peut être inconsistante face à un comportement indéfini Pourquoi ne pas vous avertir lors de l'optimisation basée sur un comportement indéfini? est une bonne lecture.

    Notez que les -fno-aggressive-loop-optimizations sont documentées dans les notes de publication de gcc 4.8 .

    Comme vous incrémentez di avant de l’utiliser pour indexer mc , la quasortingème fois dans la boucle, vous ferez référence à mc [4], qui a dépassé la fin de votre tableau, ce qui pourrait entraîner des problèmes.

    C’est parce que di ++ est exécuté sur 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 [] lorsque di == 4, il s'agit donc d'un problème hors limites, détruisant potentiellement une partie de la stack et corrompant la variable di.

    une solution serait:

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

    Tu as ceci:

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

    Essayez plutôt ceci:

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

    MODIFIER:

    Pour clarifier ce qui se passe Permet de décomposer l'itération de votre boucle For:

    1ère itération: Initialement, di est défini sur 0. Contrôle comparatif: di inférieur à 4? Oui, ça va. Incrémente par 1. Maintenant di = 1. Saisissez le "nième" élément de mc [] et définissez-le comme étant delta. Cette fois, nous prenons le 2ème élément car cette valeur indexée est 1 et non 0. Effectuez enfin le / les bloc de code dans la boucle for.

    2ème itération: Maintenant, di est défini sur 1. Vérification de la comparaison: di inférieur à 4? Oui et continuez Incrémenter di par 1. Maintenant di = 2. Prenez le "nième" élément de mc [] et définissez-le comme étant delta. Cette fois, nous prenons le 3ème élément car cette valeur indexée est 2. Effectuez enfin le / les blocs de code dans la boucle for.

    3ème itération: Maintenant, di est défini sur 2. Vérification de la comparaison: di inférieur à 4? Oui et continuez Incrémenter di par 1. Maintenant di = 3. Prenez le "nième" élément de mc [] et définissez-le comme étant delta. Cette fois, nous prenons le 4ème élément car cette valeur indexée est 3. Effectuez enfin le / les bloc de code dans la boucle for.

    4ème itération: Maintenant, di est défini sur 3. Vérification de la comparaison: di inférieur à 4? Oui et continuez Incrémentation de 1. Maintenant di = 4. (Pouvez-vous voir où cela va?) Prenez le "nième" élément de mc [] et définissez-le comme étant delta. Cette fois, nous prenons le 5ème élément car cette valeur indexée est 4. Uh Oh, nous avons un problème; notre taille de tableau est seulement 4. Delta a maintenant des ordures et c'est un comportement indéfini ou la corruption. Enfin, effectuez le ou les blocs de code dans la boucle for en utilisant "delta de mémoire".

    5ème itération Maintenant, di est défini sur 4. Vérification de la comparaison: di inférieur à 4? Non, sortez de la boucle.

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