L'exemple apocryphe classique de "comportement non défini" est, bien sûr, celui des "démons nasaux" - une impossibilité physique, indépendamment de ce que les normes C et C++ autorisent.
Parce que les communautés C et C++ ont tendance à mettre l'accent sur l'imprévisibilité du comportement non défini et sur l'idée que le compilateur est autorisé à faire faire au programme des choses littérales tout ce qui est lorsque l'on rencontre un comportement non défini, j'avais supposé que la norme n'imposait aucune restriction sur le comportement de, eh bien, le comportement non défini.
Mais le La citation pertinente de la norme C++ semble être la suivante :
[C++14: defns.undefined]:
[..] Le comportement non défini autorisé va de l'ignorance totale de la situation avec des résultats imprévisibles, au comportement pendant la traduction ou l'exécution du programme d'une manière documentée et caractéristique de l'environnement (avec ou sans émission d'un message de diagnostic), en passant par l'interruption de la traduction ou de l'exécution (avec émission d'un message de diagnostic). [..]
Il s'agit en fait d'un petit ensemble d'options possibles :
- Ignorer la situation -- Oui, la norme poursuit en disant que cela aura des "résultats imprévisibles", mais ce n'est pas la même chose que le compilateur en insérant (ce qui, je suppose, serait un prérequis pour, vous savez, les démons nasaux).
- Se comporter d'une manière documentée et caractéristique de l'environnement -- cela semble en fait relativement bénin. (Je n'ai certainement pas entendu parler de cas documentés de démons nasaux).
- Interrompre la traduction ou l'exécution -- avec un diagnostic, pas moins. Si seulement toutes les UB se comportaient aussi bien.
Je suppose que dans la plupart des cas, les compilateurs choisissent d'ignorer le comportement non défini ; par exemple, lors de la lecture d'une mémoire non initialisée, il serait vraisemblablement anti-optimisation d'insérer un quelconque code pour assurer un comportement cohérent. Je suppose que les types plus étranges de comportement non défini (tels que " voyage dans le temps ") relèverait de la deuxième catégorie - mais cela exige que ces comportements soient documentés et " caractéristiques de l'environnement " (donc je suppose que les démons nasaux ne sont produits que par des ordinateurs infernaux ?).
Ai-je mal compris la définition ? S'agit-il de simples exemples de ce qui pourrait constituer un comportement non défini, plutôt qu'une liste complète d'options ? L'affirmation selon laquelle "tout peut arriver" est-elle simplement considérée comme un effet secondaire inattendu de l'ignorance de la situation ?
Deux petits points de clarification :
- Je pensais que la question initiale était claire, et je pense que c'est le cas pour la plupart des gens, mais je vais quand même l'expliquer : Je réalise que "démons nasaux" est une plaisanterie.
- Veuillez ne pas écrire une (autre) réponse expliquant que UB permet des optimisations de compilateur spécifiques à la plate-forme, à moins que vous ne également expliquer comment il permet des optimisations qui définie par la mise en œuvre comportement ne serait pas permettre.
Cette question n'était pas destinée à servir de forum de discussion sur les (dé)mérites d'un comportement indéfini, mais c'est un peu ce qu'elle est devenue. Dans tous les cas, ce fil de discussion à propos d'un hypothétique compilateur C sans comportement indéfini peut présenter un intérêt supplémentaire pour ceux qui pensent que ce sujet est important.
3 votes
Il s'agit en fait de différences entre les systèmes d'exploitation. Par exemple, la mémoire est-elle initialisée à zéro ? Y a-t-il un gardien de pile actif ? Utilise-t-il la randomisation de l'adresse ? La spécification est silencieuse car différents comportements sont possibles. Y compris une grue.
15 votes
Le comportement indéfini est toujours une blague jusqu'à ce que quelqu'un est incinéré
5 votes
Au lieu de "démons nasaux", j'aime dire qu'un comportement indéfini peut appeler votre ex.
1 votes
Il m'arrive de mettre sur le compte d'UB le carton de lait vide dans le réfrigérateur.
8 votes
"Le comportement indéfini admissible va de l'ignorance totale de la situation avec résultats imprévisibles " Je pense que cela couvre à peu près tout sous le soleil.
1 votes
@juanchopanza Comme mentionné dans la question, ce n'est pas parce que les résultats sont imprévisibles que le compilateur peut faire tout ce qui est .
2 votes
@KyleStrand Dans ce cas, ce n'est pas ce que fait le compilateur, mais ce qui se passe lorsque le programme s'exécute. Ainsi, tout ce qui peut arriver peut arriver à l'ordinateur sur lequel le programme est exécuté.
0 votes
@juanchopanza Je comprends cela. Mais, par exemple, une ancienne version de GCC était censée lancer NetHack lorsque (certains) UB étaient rencontrés. C'est pas "en ignorant la situation."
9 votes
Juste pour savoir comment utiliser l'anglais en général, si quelqu'un dit "Notre agence de voyage propose des vacances de l'Australie au Canada en passant par la Turquie". - cela ne signifie pas que ce sont les seuls pays disponibles ; il n'y a aucune implication que la liste soit exhaustive.
0 votes
@KyleStrand Il est fort probable qu'en ignorant la situation, quelque chose d'imprévisible (au sens de la norme C++) se produise. À moins que ce comportement ne soit documenté, ce dont je doute.
3 votes
@juanchopanza Le passage que j'ai cité dit qu'il faut ignorer la situation. avec des résultats imprévisibles, ne pas l'ignorer et ensuite en faisant quelque chose d'imprévisible.
0 votes
Voici mon lien préféré concernant UB. Il traite spécifiquement des cas de course, mais je l'aime parce qu'il montre le genre de choses qu'un compilateur pourrait choisir de faire lorsqu'il est confronté à l'UB. C'est aussi très drôle ! software.intel.com/fr/us/blogs/2013/01/06/
1 votes
De plus, comme le lancement de NetHack était presque certainement un Easter egg intentionnel, ce n'est pas, par définition, un résultat "imprévisible" d'UB. Mais cela n'a pas particulièrement d'importance, puisque la liste n'est pas exhaustive.
0 votes
@TonyD Je suppose. Il semblait beaucoup plus spécifique quand je l'ai lu la première fois.
0 votes
@KyleStrand Vous devriez peut-être expliquer pourquoi vous pensez que "ignorer la situation avec des résultats imprévisibles" est différent de "l'ignorer puis faire quelque chose d'imprévisible".
2 votes
La première implique que tout résultat "imprévisible" est strictement causé par l'état imprévisible du système lorsque la situation se produit et par la non-action du programme qui ignore la situation. La seconde implique que le programme effectue en réalité une sorte de extra action à cause de la situation initiale.
0 votes
Je ne pense pas que la première implique une telle chose.
0 votes
Gardez toujours une boîte de RAID à portée de main près de votre ordinateur, juste au cas où.....
1 votes
L'UB peut être pire que les démons nasaux.
0 votes
Personnellement, je préfère "rendre votre chatte enceinte" et "rendre votre moustache bleue" à "démons nasaux".
0 votes
Ou envoyer un email à la lune.
0 votes
Ou le meurtre de votre chat .
2 votes
Vous faites seulement référence à comportement non défini au moment de la compilation qui n'est qu'une partie de l'histoire. Considérez également le comportement indéfini au moment de l'exécution, où, par exemple, un accès mémoire errant pourrait écrire une adresse mappée en mémoire qui est connectée à un circuit permettant une mine terrestre sous votre chaise.
1 votes
@smci Non, ma question porte sur l'UB en général.
0 votes
...et bien quand vous demandez "permis tout ce qui est de se produire", le temps de compilation est moins que la moitié de l'histoire. Les véritables effets secondaires se produisent au moment de l'exécution, lorsque l'exécutable tourne sur un système d'exploitation (ou une machine virtuelle) particulier, en tant que processus, sur une architecture particulière (par exemple x86), probablement avec d'autres processus et données également... la norme ne parle pas de ces possibilités.
1 votes
@smci ..... ? La question est toujours sur l'UB en général. Il n'y a rien qui indique le contraire.
1 votes
...mais cela n'a pas plus de sens de faire référence à la norme C/C++ qu'à un manuel Windows ou Unix, ou à une spécification du processeur, ou à un article sur les comportements indéfinis ou malveillants, ou à une spécification des autres processus qui pourraient être en cours d'exécution (par exemple le navigateur) et de la façon de les exploiter.
1 votes
@smci Je ne comprends pas votre objection. La norme définit le code que le compilateur est autorisé à générer ; le code généré contrôle l'éventail des possibilités d'exécution, à l'exception de circonstances mécaniques extraordinaires telles qu'une puce défectueuse. Ma question porte sur ce que le compilateur est autorisé à faire dans les cas définis par la norme comme UB - sans tenir compte du fait que le code généré par le compilateur est un code de base. effets de cette décision sont visibles au moment de la compilation ou de l'exécution.
0 votes
...et vous ne pouvez pas savoir si "quelque chose" peut se produire sans connaître le comportement d'exécution dudit code (que se passe-t-il si vous émettez un code mauvais/indéfini/malveillant mais que l'OS/VM/CPU l'attrape ? Est-ce que "quelque chose" s'est réellement produit ou non ?). Ou bien vous demandez "Est-il possible de faire en sorte que le compilateur émette tout code objet spécifique désiré que nous pourrions vouloir émettre ?" Fondamentalement, votre phrase "permettre tout ce qui est de se produire" est beaucoup trop vague quant à ce qui se "produit" et à quelle phase.
0 votes
Laissez-nous continuer cette discussion dans le chat .
1 votes
Si votre appareil CPAP a un bug logiciel, les démons nasaux ne sont pas à exclure...
0 votes
Est-ce que le comme si permettre au compilateur d'insérer le code qu'il veut tant qu'il préserve le comportement observable ? Et si le code résulte en un comportement non défini, même sous la ignorer la situation donne toujours au compilateur la permission de faire ce qu'il veut ?
1 votes
@jxh : La norme le permet. Moi, et beaucoup d'autres personnes, considérons que la norme est défectueuse car elle ne décrit pas de comportements normatifs pour les cas où les implémentations sont capables d'offrir des garanties plus fortes que celles exigées par la norme, à un coût bien inférieur à celui nécessaire à l'application pour faire face à l'absence de telles garanties.
0 votes
@jxh Si (comme je le pensais) la norme exigeait effectivement des compilateurs qu'ils choisissent l'une de ces options pour chaque cas de BU, alors je ne suis pas sûr que la règle du "comme si" ferait une grande différence ; lancer NetHack, par exemple, est un BU parfaitement légitime tel qu'il est défini, mais cela ne pourrait pas être défendu comme un cas où le compilateur se serait comporté "comme si" il avait simplement ignoré la situation. Ceci n'est pas pertinent, cependant, puisque j'ai mal compris la norme.
0 votes
@MarkRansom Vraiment ? Définissez "démon". (C'était une blague, non ?)
0 votes
@MarkRansom a peut-être voulu dire que quelque chose sortait de votre nez pendant qu'il vous tuait.
0 votes
Prenons l'exemple d'une machine hypothétique où la déréférence du pointeur NULL a entraîné le chargement d'une ROM qui s'est avérée être programmée avec NetHack. Ensuite, un compilateur émule le comportement lorsque le code est porté sur une autre plate-forme.
0 votes
Maintenant, il suffit que quelqu'un écrive un compilateur qui déclenche une panique du noyau en cas de comportement non défini.
0 votes
@nyuszika7h : Ce serait bénin par rapport aux types d'optimiseurs que certains auteurs de compilateurs privilégient. Sur une machine à architecture Harvard dont le code est exécuté à partir de la ROM, on pourrait penser que la pire conséquence imaginable d'un comportement indéfini serait d'écraser toute la RAM et les registres avec la combinaison de valeurs la plus vexatoire possible, mais l'UB hyper-moderne va au-delà. Étant donné
if (should_launch_missiles()) { arm_missiles(); if (should_really_launch_missiles()) launch_missiles();} disarm_missiles();
, un compilateur qui peut déterminer que UB se produira en suivant ce code, et que...0 votes
...
disarm_missiles()
reviendra toujours sans sortir ou se suspendre, on pourrait le remplacer parshould_arm_missiles(); arm_missiles(); should_really_launch_missiles(); launch_missiles();
sur la base du fait que si l'un des tests renvoie un résultat faux, le comportement sera indéfini. Si l'UB après le code original devait randomiser la RAM et les registres, il pourrait faire en sorte que l'exécution aille directement àlaunch_missiles()
mais un verrouillage matériel externe empêcherait le lancement lorsqu'il n'est pas armé. Si l'exécution était allée jusqu'àarm_missiles()
leshould_really_launch_missiles()
Le contrôle aurait également empêché le lancement.