Je suis surpris que personne n'ait cité le C11 spec encore. Je m'excuse pour cette longue citation, mais je pense qu'elle est pertinente.
7.5 Erreurs
L'en-tête définit plusieurs macros...
...et
errno
qui se développe en une lvalue modifiable(201) de type int
et le fil local durée de stockage, dont la valeur est fixée à un nombre d'erreur positif par plusieurs fonctions de la bibliothèque. Si une définition de macro est supprimée afin d'accéder à un objet réel, ou si un programme définit un identifiant avec le nom errno
le comportement est indéfini.
La valeur de errno
dans le fil initial est zéro au démarrage du programme (la valeur initiale de errno
dans d'autres threads est une valeur indéterminée), mais n'est jamais mise à zéro par une fonction de bibliothèque. (202) La valeur de errno peut être fixée à une valeur non nulle par un appel de fonction de qu'il y ait ou non une erreur, à condition que l'utilisation de l'option errno
n'est pas documentée dans la description de la fonction dans la présente norme internationale.
(201) La macro errno
ne doit pas nécessairement être l'identifiant d'un objet. Il peut s'étendre à un valeur lval modifiable résultant d'un appel de fonction (par exemple, *errno()
).
(202) Ainsi, un programme qui utilise errno
pour le contrôle d'erreur doit le mettre à zéro avant un appel de fonction de bibliothèque, puis l'inspecter avant un appel de fonction de bibliothèque ultérieur. Sur Bien entendu, une fonction de bibliothèque peut enregistrer la valeur de errno
à l'entrée, puis le mettre à zéro, tant que la valeur originale est restaurée si errno
La valeur de l'objet est encore nulle juste avant le retour.
"Fil local" signifie register
est sorti. Type int
signifie que les champs de bits sont hors jeu (IMO). Donc &errno
ça me semble légal.
L'utilisation persistante de mots comme "il" et "il". le site valeur" suggère que les auteurs de la norme n'ont pas envisagé de &errno
étant non-constant. Je suppose que l'on pourrait imaginer une implémentation où &errno
n'était pas constant au sein d'un fil de discussion particulier, mais pour être utilisé de la façon dont les notes de bas de page le disent (mettre à zéro, puis vérifier après l'appel de la fonction de bibliothèque), il devrait être délibérément contradictoire, et éventuellement nécessiter un support de compilateur spécialisé juste pour être contradictoire.
En bref, si la spécification autorise une valeur non constante pour la fonction &errno
je ne pense pas que ce soit délibéré.
[mise à jour]
R. pose une excellente question dans les commentaires. Après y avoir réfléchi, je crois maintenant connaître la bonne réponse à sa question, ainsi qu'à la question initiale. Voyons si je peux vous convaincre, cher lecteur.
R. fait remarquer que GCC permet quelque chose comme cela au niveau supérieur :
register int errno asm ("r37"); // line R
Cela permettrait de déclarer errno
comme une valeur globale contenue dans le registre r37
. De toute évidence, il s'agirait d'une lvalue modifiable au niveau du thread. Ainsi, une implémentation C conforme pourrait-elle déclarer errno
comme ça ?
La réponse est pas de . Lorsque vous ou moi utilisons le mot "déclaration", nous avons généralement à l'esprit un concept familier et intuitif. Mais la norme ne s'exprime pas de manière familière ou intuitive ; elle parle précisément Elle vise à n'utiliser que des termes bien définis. Dans le cas de "déclaration", la norme elle-même définit le terme ; et lorsqu'elle utilise ce terme, elle utilise sa propre définition.
En lisant la spécification, vous pouvez apprendre précisément ce qu'est une "déclaration" et ce qu'elle est précisément no . En d'autres termes, la norme décrit le langage "C". Elle ne décrit pas "un langage qui n'est pas C". En ce qui concerne la norme, "C avec extensions" est simplement "un langage qui n'est pas C".
Ainsi, du point de vue de la norme, la ligne R n'est pas du tout une déclaration . Il n'est même pas analysé ! Il pourrait aussi bien être lu :
long long long __Foo_e!r!r!n!o()blurfl??/**
En ce qui concerne la spécification, il s'agit tout autant d'une "déclaration" que la ligne R, c'est-à-dire pas du tout.
Donc, quand la spécification C11 dit, dans la section 6.5.3.2 :
L'opérande de la fonction unaire &
L'opérateur est soit une fonction le résultat d'une fonction []
ou unaire *
ou une lvalue qui désigne un objet qui n'est pas un bit-field et qui n'est pas déclaré avec le le registre storage-class specifier.
...ça veut dire quelque chose de très précis qui ne... no se référer à quelque chose comme la ligne R.
Maintenant, considérez la déclaration de la int
objet à laquelle errno
se réfère. (Remarque : je ne parle pas de la déclaration de la errno
nom car, bien entendu, une telle déclaration pourrait ne pas exister si errno
est, disons, une macro. Je veux dire que la déclaration du sous-jacent int
objet).
Le langage ci-dessus indique que vous pouvez prendre l'adresse d'une valeur l à moins qu'elle ne désigne un champ de bits ou qu'elle désigne un objet "déclaré". register
. Et la spécification pour le sous-jacent errno
l'objet dit qu'il est modifiable int
lvalue avec une durée locale.
Maintenant, il est vrai que la spécification ne dit pas que le sous-jacent errno
doit être déclaré du tout. Peut-être qu'il apparaît simplement par une magie de compilateur définie par l'implémentation. Mais encore une fois, quand la spécification dit "déclaré avec le spécificateur de classe de stockage de registre", elle utilise sa propre terminologie.
Donc, soit le sous-jacent errno
l'objet est "déclaré" au sens classique du terme, auquel cas il ne peut pas être à la fois register
et thread-local ; ou il n'est pas déclaré du tout dans ce cas, il n'est pas déclaré register
. De toute façon, comme il s'agit d'une lvalue, vous pouvez prendre son adresse.
(Sauf s'il s'agit d'un champ de bits, mais je pense que nous sommes d'accord sur le fait qu'un champ de bits n'est pas un objet de type int
.)