C LanguageComportement non défini


Introduction

En C, certaines expressions génèrent un comportement indéfini . Le standard choisit explicitement de ne pas définir comment un compilateur doit se comporter s'il rencontre une telle expression. En conséquence, un compilateur est libre de faire ce qu’il juge nécessaire et peut produire des résultats utiles, des résultats inattendus, voire un crash.

Le code qui appelle UB peut fonctionner comme prévu sur un système spécifique avec un compilateur spécifique, mais ne fonctionnera probablement pas sur un autre système, ou avec un compilateur, une version de compilateur ou des paramètres de compilateur différents.

Remarques

Qu'est-ce qu'un comportement indéfini (UB)?

Le comportement non défini est un terme utilisé dans la norme C. La norme C11 (ISO / IEC 9899: 2011) définit le terme comportement indéfini comme

comportement, lors de l'utilisation d'une structure de programme non portable ou erronée ou de données erronées, pour lesquels la présente Norme internationale n'impose aucune exigence

Que se passe-t-il s'il y a UB dans mon code?

Ce sont les résultats qui peuvent survenir en raison d’un comportement indéfini selon la norme:

NOTE Le comportement indéfini possible peut aller 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), à la fin d'une traduction émission d'un message de diagnostic).

La citation suivante est souvent utilisée pour décrire (de manière moins formelle) des résultats provenant d'un comportement non défini:

"Lorsque le compilateur rencontre [une construction indéfinie donnée], il est légal de faire voler des démons" (l'implication est que le compilateur peut choisir n'importe quelle manière arbitraire d'interpréter le code sans violer la norme ANSI C)

Pourquoi UB existe-t-il?

Si c'est si mauvais, pourquoi ne l'ont-ils pas simplement défini ou défini?

Un comportement non défini offre davantage d'opportunités d'optimisation; Le compilateur peut légitimement supposer que tout code ne contient pas de comportement indéfini, ce qui lui permet d'éviter les vérifications à l'exécution et d'effectuer des optimisations dont la validité serait coûteuse ou impossible à prouver autrement.

Pourquoi UB est-il difficile à retrouver?

Il existe au moins deux raisons pour lesquelles un comportement non défini crée des bogues difficiles à détecter:

  • Le compilateur n'est pas obligé de vous avertir - et ne peut généralement pas le faire de manière fiable - d'un comportement non défini. En fait, l'exiger de le faire irait directement à l'encontre de la raison d'être d'un comportement indéfini.
  • Les résultats imprévisibles pourraient ne pas commencer à se dérouler au point exact de l’opération où se produit la construction dont le comportement n’est pas défini; Un comportement non défini entrave toute l'exécution et ses effets peuvent survenir à tout moment: pendant, après ou même avant la construction indéfinie.

Considérez dereference pointeur nul: le compilateur n'est pas obligé de diagnostiquer le déréférencement de pointeur nul, et même ne pourrait pas, car à l'exécution, tout pointeur passé dans une fonction ou dans une variable globale peut être nul. Et lorsque le déréférencement de pointeur nul se produit, la norme ne prescrit pas que le programme doive se bloquer. Au lieu de cela, le programme peut tomber en panne plus tôt, plus tard ou ne pas se bloquer du tout; Il pourrait même se comporter comme si le pointeur null indiquait un objet valide et se comporter complètement normalement, uniquement pour se bloquer dans d'autres circonstances.

Dans le cas de déréférencement de pointeur nul, le langage C diffère des langages gérés tels que Java ou C #, où le comportement du déréférencement de pointeur nul est défini : une exception est levée à l'heure exacte ( NullPointerException en Java, NullReferenceException en C #) ainsi, ceux venant de Java ou de C # peuvent croire de manière incorrecte que dans un tel cas, un programme C doit tomber en panne, avec ou sans émission d'un message de diagnostic .

Information additionnelle

Plusieurs situations de ce type doivent être clairement distinguées:

  • Comportement explicitement indéfini, c'est-à-dire que le standard C vous indique explicitement que vous êtes hors limites.
  • Comportement implicitement indéfini, où il n'y a tout simplement pas de texte dans la norme prévoyant un comportement pour la situation dans laquelle vous avez amené votre programme.

Rappelez-vous également que dans de nombreux endroits, le comportement de certaines constructions est délibérément indéfini par le standard C pour laisser la place aux développeurs de bibliothèques et de compilateurs de proposer leurs propres définitions. Un bon exemple est celui des signaux et des gestionnaires de signaux, où les extensions de C, telles que la norme du système d'exploitation POSIX, définissent des règles beaucoup plus élaborées. Dans de tels cas, il vous suffit de vérifier la documentation de votre plate-forme; la norme C ne peut rien vous dire.

Notez également que si un comportement indéfini se produit dans le programme, cela ne signifie pas que le seul point où un comportement non défini a eu lieu est problématique, mais un programme entier devient sans signification.

En raison de telles préoccupations, il est important (surtout que les compilateurs ne nous avertissent pas toujours sur UB) pour que la programmation de personnes en C soit au moins familière avec le genre de choses qui déclenchent un comportement indéfini.

Il convient de noter que certains outils (par exemple des outils d’analyse statique tels que PC-Lint) facilitent la détection d’un comportement indéfini, mais là encore, ils ne peuvent pas détecter toutes les occurrences d’un comportement indéfini.

Comportement non défini Exemples Liés