Un cas bien connu de comportement indéfini résulte de débordements lors d'opérations arithmétiques sur des entiers : ../Sujets/Maths/Nombres-entiers.html#debordement
Plusieurs langages n'ont pas le concept de comportement indéfini, ou Undefined Behavior, mais ce concept est important dans un langage axé sur la vitesse d'exécution à tout prix (ou presque) comme C ou C++. Dans ces langages, en effet, la plupart des expressions et des énoncés admissibles mènent à un comportement défini par le standard du langage, mais certaines opérations ont un résultat non-spécifié ou indéfini, donc pour lesquels les choix peuvent varier d'un compilateur à l'autre.
La définition technique du comportement indéfini en C++ est : http://eel.is/c++draft/defns.undefined
Le présent document propose quelques lectures pour mieux comprendre ce que signifie le terme Undefined Behavior, qui contribue à l'atteinte (ou non) d'une forme de Type-Safety dans un langage.
« Undefined behavior isn't evil. Its chaotic neutral » – Bryce Lelbach (Source)
Quelques exemples de comportement indéfini et de ses impacts, empruntés pour la plupart à Ville Voutilainen.
Dans cet exemple, le code généré n'appellera jamais h() car *p a été utilisé au prélable sans valider que p soit non-nul (voir https://godbolt.org/z/uDUwV5 pour une démonstration). Le if() qui semble redondant sur cette base sera éliminé. Aux yeux du compilateur, sur la base du code source, il est clair que p est non-nul C'est un cas où le passé influence l'avenir |
|
Dans cet exemple (la différence avec le précédent n'est que d'un seul caractère!), l'appel à h() sera toujours fait et le test de p sera élidé (voir https://godbolt.org/z/e6rNPq pour une démonstration). Le raisonnement derrière cette optimisation sera essentiellement le même que pour l'exemple précédent C'est un autre cas où le passé influence l'avenir |
|
Dans cet exemple, l'appel à h() ne sera jamais fait et le test de p sera élidé, car plus tard dans la fonction, on utilise *p sans avoir validé p (voir https://godbolt.org/z/OXHyd- pour une démonstration). Aux yeux du compilateur, sur la base du code source, il est clair que p est non-nul C'est un cas où le futur influence le passé |
|
Dans cet exemple, l'appel à h() sera toujours fait et le test de p sera élidé, car plus tard dans la fonction, on utilise *p sans avoir validé p (voir https://godbolt.org/z/AqebPk pour une démonstration). Aux yeux du compilateur, sur la base du code source, il est clair que p est non-nul C'est un autre cas où le futur influence le passé |
|
Exemple plus complexe (voir https://godbolt.org/z/Nm-GOj pour une démonstration) :
Les compilateurs sont devenus très, très efficaces... |
|
Exemple intéressant proposé par Andrzej Krzemienski (voir https://godbolt.org/z/62VleR pour une démonstration). Le programme souhaite comptabiliser les appels à f(p) où p est nul, or f(p) appelle impl(p) qui, pour sa part, utilise *p sans valider que p soit non-nul. Le futur influence le passé, et le compilateur supprimera le test sur p fait par f() du fait qu'il semble manifestement inutile |
|