Énigme de compilation C avec comportement de boucle imprévisible
Le code suivant présente un problème de compilation intrigant :
<code class="c++">#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; }</code>
Contraire à la sortie attendue de "0, 1, 2, 3", le code produit une répétition sans fin de "0, 1, 2, 3, 4, 5, ...". Après enquête, il semble que la comparaison di < 4 ne fonctionne pas correctement, ce qui entraîne une boucle infinie.
La désactivation de l'affectation apparemment anodine delta = mc[di] résout comme par magie le problème, générant le résultat souhaité. Quel est le problème que pose cette simple action ?
Plonger dans un comportement indéfini
La clé pour percer ce mystère réside dans la compréhension d'un comportement indéfini. L'affectation delta = mc[di] déclenche un accès à un tableau hors limites lors de la dernière itération de la boucle. Bien que de nombreux compilateurs puissent supposer qu'aucun comportement indéfini n'est basé sur des stratégies d'optimisation, cette hypothèse est intrinsèquement erronée.
GCC, avec les optimisations activées, peut optimiser de manière agressive la boucle en supposant l'absence de comportement indéfini. Cette optimisation conduit à une inférence erronée selon laquelle di < 4 est toujours vrai, car un accès hors limites au tableau constituerait un comportement indéfini.
En conséquence, la boucle devient infinie, car la condition di < 4 est toujours respecté. Pour éviter cette optimisation erronée, l'indicateur -fno-aggressive-loop-optimizations peut être ajouté à GCC.
Dévoilement du fonctionnement interne du compilateur
Un examen plus approfondi du le code optimisé révèle que le di < 4 check est supprimé et remplacé par une instruction jmp inconditionnelle. Ce comportement s'aligne sur l'hypothèse d'aucun comportement indéfini, ce qui entraîne une boucle infinie.
Contrairement à ce comportement, Clang avec -fsanitize=undefined attraperait ce cas. Cependant, GCC avec le même indicateur ne parvient pas à émettre un avertissement dans ce cas spécifique.
Les périls d'un comportement non défini
Comportement non défini, tel que défini par la norme C, permet des résultats imprévisibles, y compris en ignorant complètement la situation. Un comportement non défini doit être évité à tout prix, car il peut entraîner à la fois des incohérences du compilateur et un comportement d'exécution inattendu.
Pour éviter de tels pièges, les programmeurs doivent s'efforcer d'obtenir un code bien défini qui adhère à la sémantique prévue. Les compilateurs doivent également mettre en œuvre des défenses appropriées pour alerter les développeurs d'un comportement potentiel non défini.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!