NOTE: Ceci est une question c , bien que j’ai ajouté c ++ au cas où un expert en C++ peut fournir une justification ou une raison historique pour laquelle C++ utilise un libellé différent de C.
Dans la spécification de la bibliothèque standard C, nous avons ce texte normatif, C17 7.1.3 Identifiants réservés (emphase mien):
- Tous les identifiants commençant par un trait de soulignement et une lettre majuscule ou un autre trait de soulignement sont toujours réservés pour toute utilisation .
- Tous les identificateurs commençant par un trait de soulignement sont toujours réservés à une utilisation en tant qu'identificateurs avec une étendue de fichier dans les espaces de nom de balise et ordinaire.
Maintenant, je continue à lire les réponses sur SO) par divers experts estimés du langage C, qui affirment qu’il est acceptable pour un compilateur ou une bibliothèque standard d’utiliser des identificateurs avec soulignement + majuscule ou double soulignement.
"Réservé à un usage quelconque" ne signifie-t-il pas réservé à n'importe qui sauf aux futures extensions du langage C lui-même? Cela signifie que l'implémentation est pas autorisée à les utiliser.
Alors que la deuxième phrase ci-dessus, concernant le trait de soulignement simple semble être dirigée vers la mise en œuvre?
En général, la norme C est écrite de manière à ce que les vendeurs de compilateur/implémenteurs de librairies soient le lecteur type, et non les programmeurs d’applications.
Notamment, C++ a une formulation très différente:
- Chaque nom contenant un double trait de soulignement (
__
) ou commence par un trait de soulignement suivi d'une lettre majuscule (2.11) est réservé à la mise en oeuvre pour toute utilisation .
(Voir Quelles sont les règles d'utilisation d'un trait de soulignement dans un identifiant C++? )
Est-ce peut-être une confusion entre C et C++ et les langages sont différents ici?
Dans la norme C, le sens du terme "réservé" est défini par 7.1.3p2, immédiatement sous la liste à puces que vous citez:
Aucun autre identifiant n'est réservé. Si le programme déclare ou définit un identifiant dans un contexte dans lequel il est réservé (autre que celui autorisé par 7.1.4), ou définit un identifiant réservé en tant que nom de macro, le comportement est indéfini.
Mine d'emphase: les identifiants réservés imposent une restriction sur le programme, pas sur l'implémentation. Ainsi, l'interprétation commune - les identificateurs réservés peuvent être utilisés par la mise en oeuvre à n'importe quelle fin - est correcte pour C.
Je n'ai pas suivi le standard C++ et je ne me sens plus qualifié pour l'interpréter.
Alors que la norme est principalement destinée à guider les développeurs, elle est écrite comme en tant que description de ce qui fait qu'un programme est bien formé et quel est son effet. En effet, la définition de base d'un compilateur conforme aux normes est celle qui fonctionne correctement pour tout programme conforme aux normes:
Un programme strictement conforme n'utilisera que les fonctionnalités de la langue et de la bibliothèque spécifiées dans la présente Norme internationale ... Une implémentation hébergée conforme acceptera tout programme strictement conforme.
Lu séparément, ceci est extrêmement restrictif des extensions d’un compilateur. Par exemple, en se basant uniquement sur cette clause, un compilateur ne devrait pas pouvoir définir aucun de ses propres mots réservés. Après tout, tout mot donné par un compilateur pourrait vouloir réserver, pourrait néanmoins apparaître dans un programme strictement conforme, forçant la main du compilateur.
La norme continue cependant:
Une mise en œuvre conforme peut avoir des extensions (y compris des fonctions de bibliothèque supplémentaires), à condition qu'elles ne modifient pas le comportement d'un programme strictement conforme.
C'est la pièce clé. Les extensions du compilateur doivent être écrites de manière à affecter les programmes non conformes (ceux qui contiennent un comportement indéfini, ou qui ne devraient même pas compiler du tout) , leur permettant de compiler et de faire des choses amusantes.
Donc, le but de définir des "identifiants réservés", lorsque la langue ne nécessite pas ces identifiants, est de donner aux implémentations une marge de manœuvre supplémentaire en en leur fournissant certaines choses qui rendent un programme non conforme. La raison pour laquelle un compilateur peut reconnaître, par exemple, __declspec
dans une déclaration est parce que mettre __declspec
dans une déclaration est autrement illégale, le compilateur est donc autorisé à faire ce qu'il veut!
L'importance de "réservé à tout usage" est donc qu'il ne laisse aucune question sur le pouvoir d'un compilateur de traiter de tels identificateurs comme ayant une signification quelconque. La compatibilité future est une préoccupation relativement éloignée.
La norme C++ fonctionne de la même manière, bien que le gambit soit un peu plus explicite:
Une mise en œuvre conforme peut avoir des extensions (y compris des fonctions de bibliothèque supplémentaires), à condition qu’elles ne modifient pas le comportement d’un programme bien formé. Les implémentations sont nécessaires pour diagnostiquer les programmes qui utilisent de telles extensions mal formées conformément à la présente Norme internationale. Ceci fait, ils peuvent cependant compiler et exécuter de tels programmes.
Je soupçonne que la différence de formulation réside dans la norme C++, tout en précisant la manière dont les extensions sont censées fonctionner. Néanmoins, rien dans la norme C n'empêche une mise en oeuvre de faire la même chose. (Et nous ignorons tous fondamentalement l’obligation pour le compilateur de vous avertir chaque fois que vous utilisez __declspec
.)
En ce qui concerne la différence de formulation entre C et C++, je publie ici ma propre petite recherche à titre de référence:
Le début K & R C 1ère édition a ce texte:
... les noms qui ne sont utilisés que par les fonctions de la bibliothèque commencent par un trait de soulignement, ce qui les rend moins susceptibles d'entrer en conflit avec les noms du programme de l'utilisateur.
K & R 2nd edition a ajouté une annexe B qui traite de la bibliothèque standard, où nous pouvons lire
Les identifiants externes commençant par un trait de soulignement sont réservés à l'usage de la bibliothèque, de même que tous les autres identificateurs commençant par un trait de soulignement, une lettre majuscule ou un autre trait de soulignement.
Les versions préliminaires ANSI C, ainsi que "C90" ISO 9899: 1990, ont le même texte que dans la norme ISO actuelle.
Les premiers projets de C++ ont cependant un texte différent, comme le note @hvd, ce qui pourrait indiquer une clarification du standard C. De PROJET: 20 septembre 1994 :
17.3.3.1.2 Noms globaux
...
Chaque nom commençant par un trait de soulignement et une lettre majuscule ou un autre trait de soulignement (2.8) est réservé à la mise en oeuvre pour toute utilisation.
Donc, apparemment, le terme "réservé à un usage quelconque" a été inventé par le comité ANSI/ISO C90, alors que le comité C++, quelques années plus tard, utilisait un libellé plus clair, similaire à celui du livre K & R pré-standard.
La justification de C99 V5.10 dit ceci au-dessous de 7.1.3:
Sont également réservés à l'implémenteur tous les identifiants externes commençant par un trait de soulignement et tous les autres identifiants commençant par un trait de soulignement suivi d'une lettre majuscule ou d'un soulignement. Cela donne un espace de nom pour écrire les nombreuses macros et machines externes non externes qui se trouvent dans les coulisses et dont une bibliothèque a besoin pour bien faire son travail.
Cela montre clairement l’intention du comité: "réservé à tout usage" signifie "réservé au réalisateur".
Il convient également de noter que la norme C actuelle contient le texte normatif suivant ailleurs, au 6.2.5:
Il peut également y avoir des types d'entiers signés étendus définis par la mise en œuvre. 38)
où la note de bas de page d'information 38 indique:
38) Les mots-clés définis par la mise en oeuvre doivent avoir la forme d'un identifiant réservé à un usage décrit à la section 7.1.3.
C a plusieurs contextes dans lesquels un symbole peut avoir une définition:
"Réservé à toute utilisation" signifie que le code utilisateur d'un programme conforme ne peut pas être utilisé.1 symboles commençant par un trait de soulignement suivi d'une lettre majuscule ou d'un autre trait de soulignement dans l'un des contextes ci-dessus. Comparez avec les identifiants qui commencent par un simple soulignement mais sont suivis d'un nombre minuscule ou d'un chiffre. Cela tombe dans la deuxième classe d'identificateurs qui commencent par un trait de soulignement. Le code utilisateur peut peut être utilisé avec ces identificateurs comme noms d'arguments de macro, comme étiquettes ou comme noms de membres de structure/union.
"Réservé à toute utilisation" ne signifie pas que la mise en oeuvre ne peut pas utiliser de tels symboles. Le but de la réservation est de fournir un espace de noms que les implémentations peuvent librement utiliser, sans craindre que les noms définis par l'implémentation ne soient en conflit avec les noms définis par le code d'utilisateur dans un programme conforme.
1La norme ne signifie pas vraiment "ne peut pas utiliser". La norme encourage l'utilisation programmatique d'un petit nombre de noms commençant par un double soulignement. Par exemple, une implémentation conforme est nécessaire pour définir __STDC_VERSION__
, __FILE__
, __LINE__
, et __func__
. La version 2011 de la norme donne même un exemple de programme supposé conforme qui fait référence à __func__
.
La norme C permet aux implémentations d’attacher toute signification qu’elles jugent appropriée aux identificateurs réservés. La plupart des implémentations traiteront les identifiants non reconnus de formes réservées de la même manière que tout autre identifiant reconnu lorsqu'il n'y a aucune raison de faire autrement, permettant ainsi quelque chose comme:
#ifdef __ACME_COMPILER
#define near __near
#else
#define near
#endif
int near foo;
déclarer un identifiant foo
en utilisant un __near
qualificatif si le code est en cours de traitement dans un compilateur Acme (qui prendrait en charge une telle chose), mais également compatible avec d’autres compilateurs qui n’exigeraient ni ne bénéficieraient de l’utilisation d’une telle directive. Rien n'empêcherait une implémentation conforme de définir __ACME_COMPILER
et interpréter __near
signifie "lancer des missiles nucléaires", mais une implémentation de qualité ne devrait pas faire plus que de casser le code comme ci-dessus. Si une implémentation ne sait pas quoi __ACME_COMPILER
est censé vouloir dire, le traiter comme n'importe quel autre identifiant inconnu lui permettrait de prendre en charge des constructions utiles telles que ci-dessus.
Il y a des mois de retard mais un point reste inchangé par les autres.
Votre question peut être vue de l’autre côté. La norme permet à l’implémentation (comme vous l’avez observé) d’utiliser un symbole tel que _Foo
_ mais plus important encore, interdit à l'implémentation d'utiliser foo
. Ce dernier est réservé à votre utilisation.
Pour comprendre, aux fins de la discussion, supposons qu’un futur standard C introduise le nouveau mot-clé _Foo
. L'implémentation hypothétique utilisait déjà ce symbole, que se passe-t-il?
Répondre:
Au début, l’implémentation n’aurait pas encore implémenté le nouveau standard. Jusqu'à la mise en œuvre, la nouvelle norme n'a pas d'effet pratique.
Plus tard, dans le cadre de la mise en œuvre de la nouvelle norme, la mise en œuvre change silencieusement chaque _Foo
à _Bar
.
Aucun problème.
En fait, si vous y réfléchissez de cette manière, vous pouvez dire que la manière habituelle de réserver de tels mots est presque le seul moyen de les réserver.