La réponse principale est une idée fausse (mais courante):
Le comportement non défini est une propriété d' exécution *. Il NE PEUT PAS "voyager dans le temps"!
Certaines opérations sont définies (par la norme) comme ayant des effets secondaires et ne peuvent pas être optimisées. Les opérations qui effectuent des E / S ou qui accèdent aux volatile
variables appartiennent à cette catégorie.
Cependant , il y a une mise en garde: UB peut être n'importe quel comportement, y compris un comportement qui annule les opérations précédentes. Cela peut avoir des conséquences similaires, dans certains cas, à l'optimisation du code antérieur.
En fait, cela est cohérent avec la citation de la première réponse (c'est moi qui souligne):
Une implémentation conforme exécutant un programme bien formé produira le même comportement observable que l'une des exécutions possibles de l'instance correspondante de la machine abstraite avec le même programme et la même entrée.
Cependant, si une telle exécution contient une opération indéfinie, la présente Norme internationale n'impose aucune exigence sur l'implémentation exécutant ce programme avec cette entrée (pas même en ce qui concerne les opérations précédant la première opération non définie).
Oui, cette citation ne dit « pas même en ce qui concerne les opérations précédant la première opération non définie » , mais avis que cela est spécifiquement sur le code qui est exécuté , non seulement compilé.
Après tout, un comportement indéfini qui n'est pas réellement atteint ne fait rien, et pour que la ligne contenant UB soit réellement atteinte, le code qui le précède doit s'exécuter en premier!
Donc oui, une fois UB exécuté , les effets des opérations précédentes deviennent indéfinis. Mais jusqu'à ce que cela se produise, l'exécution du programme est bien définie.
Notez, cependant, que toutes les exécutions du programme qui aboutissent à ce phénomène peuvent être optimisées pour des programmes équivalents , y compris ceux qui effectuent des opérations précédentes mais annulent ensuite leurs effets. Par conséquent, le code précédent peut être optimisé chaque fois que cela équivaudrait à ce que leurs effets soient annulés ; sinon, il ne peut pas. Voir ci-dessous pour un exemple.
* Remarque: ce n'est pas incompatible avec UB qui se produit au moment de la compilation . Si le compilateur peut effectivement prouver que le code UB sera toujours exécuté pour toutes les entrées, alors UB peut s'étendre jusqu'au moment de la compilation. Cependant, cela nécessite de savoir que tout le code précédent revient finalement , ce qui est une exigence forte. Encore une fois, voir ci-dessous pour un exemple / une explication.
Pour rendre cela concret, notez que le code suivant doit s'imprimer foo
et attendre votre entrée quel que soit le comportement non défini qui le suit:
printf("foo")
getchar()
*(char*)1 = 1
Cependant, notez également qu'il n'y a aucune garantie qui foo
restera à l'écran après que l'UB se produise, ou que le caractère que vous avez tapé ne sera plus dans la mémoire tampon d'entrée; ces deux opérations peuvent être «annulées», ce qui a un effet similaire au «voyage dans le temps» UB.
Si la getchar()
ligne n'était pas là, il serait légal que les lignes soient optimisées si et seulement si cela ne pouvait pas être distingué de la sortie foo
puis de la «non-exécution».
Que les deux soient indiscernables ou non dépendrait entièrement de l'implémentation (c'est-à-dire de votre compilateur et de votre bibliothèque standard). Par exemple, pouvez-vous printf
bloquer votre thread ici en attendant qu'un autre programme lise la sortie? Ou reviendra-t-il immédiatement?
S'il peut bloquer ici, alors un autre programme peut refuser de lire sa sortie complète, et il peut ne jamais revenir, et par conséquent UB peut ne jamais se produire réellement.
S'il peut revenir immédiatement ici, alors nous savons qu'il doit revenir, et par conséquent, l'optimiser est totalement impossible à distinguer de son exécution et de son annulation.
Bien sûr, puisque le compilateur sait quel comportement est autorisé pour sa version particulière de printf
, il peut optimiser en conséquence, et par conséquent printf
peut être optimisé dans certains cas et pas dans d'autres. Mais, encore une fois, la justification est que cela ne se distingue pas des opérations précédentes annulées par l'UB, non pas que le code précédent est "empoisonné" à cause de l'UB.
a
a