Premièrement, je noterai que bien que je ne mentionne que "C", la même chose s’applique également au C ++.
Le commentaire mentionnant Godel était en partie (mais seulement en partie) pertinent.
Lorsque vous descendez à elle, un comportement non défini dans les normes C est en grande partie tout en soulignant la limite entre ce que les tentatives standards pour définir, et ce qu'il ne fonctionne pas.
Les théorèmes de Godel (il y en a deux) disent qu'il est impossible de définir un système mathématique qui peut être prouvé (par ses propres règles) à la fois complet et cohérent. Vous pouvez faire vos règles pour qu'elles soient complètes (le cas dont il s'est occupé était les règles "normales" pour les nombres naturels), ou bien vous pouvez permettre de prouver sa cohérence, mais vous ne pouvez pas avoir les deux.
Dans le cas de quelque chose comme C, cela ne s'applique pas directement - pour la plupart, la "prouvabilité" de l'exhaustivité ou de la cohérence du système n'est pas une priorité absolue pour la plupart des concepteurs de langage. En même temps, oui, ils ont probablement été influencés (au moins dans une certaine mesure) en sachant qu'il est impossible de définir un système «parfait» - un système parfaitement complet et cohérent. Sachant qu'une telle chose est impossible a peut-être rendu un peu plus facile de prendre du recul, de respirer un peu et de décider des limites de ce qu'ils essaient de définir.
Au risque d'être (encore une fois) accusé d'arrogance, je qualifierais le standard C de régi (en partie) par deux idées de base:
- Le langage doit prendre en charge une variété de matériels aussi large que possible (idéalement, tout le matériel "sain" doit être abaissé à une limite inférieure raisonnable).
- Le langage doit permettre d’écrire le plus grand nombre possible de logiciels pour l’environnement donné.
La première signifie que si quelqu'un définit un nouveau processeur, il devrait être possible de fournir une bonne implémentation de C, solide et utilisable, à condition que sa conception se rapproche au moins raisonnablement de quelques directives simples - en gros, si suit quelque chose sur l'ordre général du modèle de Von Neumann et fournit au moins une quantité de mémoire minimale raisonnable, qui devrait être suffisante pour permettre une implémentation en C. Pour une implémentation "hébergée" (fonctionnant sur un système d'exploitation), vous devez prendre en charge une notion qui correspond de manière assez proche aux fichiers et qui a un jeu de caractères avec un certain jeu minimal de caractères (91 requis).
Le second moyen , il devrait être possible d'écrire du code qui manipule le matériel directement, de sorte que vous pouvez écrire des choses comme les chargeurs de démarrage, les systèmes d' exploitation, logiciels embarqués qui fonctionne sans système d' exploitation, etc. Il y a en fin de compte des limites à cet égard, donc presque tout système d’exploitation pratique, chargeur de démarrage, etc., est susceptible de contenir au moins un peu de code écrit en langage assembleur. De même, même un petit système intégré est susceptible d'inclure au moins une sorte de routine de bibliothèque pré-écrite pour donner accès aux périphériques sur le système hôte. Bien qu’il soit difficile de définir une limite précise, l’intention est que la dépendance à ce code soit réduite au minimum.
Le comportement non défini dans le langage est en grande partie motivé par l'intention du langage de prendre en charge ces capacités. Par exemple, le langage vous permet de convertir un entier arbitraire en pointeur et d'accéder à tout ce qui se trouve à cette adresse. La norme n'essaie pas de dire ce qui se passera quand vous le ferez (par exemple, même la lecture de certaines adresses peut avoir des effets visibles de l'extérieur). En même temps, il ne fait aucun effort pour vous empêcher de faire de telles choses, car vous devez utiliser certains types de logiciels que vous êtes censé être capable d’écrire en C.
Il existe également un comportement indéfini lié à d'autres éléments de conception. Par exemple, une autre intention de C est de prendre en charge une compilation séparée. Cela signifie (par exemple) qu'il est prévu que vous puissiez "relier" des morceaux en utilisant un éditeur de liens qui suit à peu près ce que la plupart d'entre nous voyons comme le modèle habituel d'un éditeur de liens. En particulier, il devrait être possible de combiner des modules compilés séparément dans un programme complet sans connaissance de la sémantique du langage.
Il existe un autre type de comportement indéfini (qui est beaucoup plus courant en C ++ qu'en C) et qui existe simplement à cause des limites de la technologie du compilateur - des choses que nous connaissons fondamentalement sont des erreurs et que nous voudrions probablement que le compilateur diagnostique comme telles, mais étant donné les limites actuelles de la technologie des compilateurs, il est douteux qu’elles puissent être diagnostiquées en toutes circonstances. Bon nombre d’entre elles sont dictées par d’autres exigences, telles que la compilation séparée, il s’agit donc en grande partie d’équilibrer des exigences contradictoires. Dans ce cas, le comité a généralement opté pour une plus grande capacité, même si cela ne permet pas de diagnostiquer certains problèmes éventuels, plutôt que de limiter les capacités pour assurer que tous les problèmes possibles sont diagnostiqués.
Ces différences d' intention expliquent la plupart des différences entre C et quelque chose comme Java ou les systèmes basés sur CLI de Microsoft. Ces derniers se limitent assez explicitement à travailler avec un ensemble de matériel beaucoup plus limité ou à obliger les logiciels à émuler le matériel plus spécifique qu'ils ciblent. Ils ont également pour objectif spécifique d’ empêcher toute manipulation directe du matériel, mais vous obligent plutôt à utiliser quelque chose comme JNI ou P / Invoke (et un code écrit en quelque chose comme C) pour même tenter une telle tentative.
Pour revenir un instant aux théorèmes de Godel, nous pouvons faire un parallèle: Java et CLI ont opté pour l’alternative "cohérente en interne", tandis que C a opté pour l’alternative "complète". Bien sûr, cela est une analogie très rude - je doute que quiconque de tenter une preuve formelle de soit la cohérence interne ou l' exhaustivité dans les deux cas. Néanmoins, la notion générale correspond assez étroitement aux choix qu’ils ont pris.