I am surprised that no one has quoted the C11 spec yet. Apologies for the long quote, but I think this is relevant.
7.5 Errors
The header defines several macros ...
... and
errno
which expands to modifiable lvalue (201), which has the int type and local stream storage time, the value of which is set to the number of positive errors for several library functions. If the macro definition is suppressed to access a real object or the program defines an identifier named errno , the behavior is undefined.
The errno value in the original thread is zero when the program starts (the initial errno value in other threads is an undefined value), but it is never set to zero by any function library. (202) The value of errno can be set to a non-zero value by the library of a function call, regardless of whether or not there is an error, provided that errno used, it is not described in the function description in this International Standard.
(201) The errno macro does not have to be an object identifier. It can change the value of lvalue caused by a function call (e.g. *errno() ).
(202) Thus, a program that uses errno to check for errors should set it to zero before calling the library function, then check it before calling the library again. Of course, the library function can save the errno value when writing, and then set it to zero until the original value is restored if errno s is still zero before returning.
"Local stream" means register missing. The int type means that no bitfields are available (IMO). So &errno looks legit for me.
The constant use of words like "this" and "meaning" suggests that the authors of the standard did not consider &errno as inconsistent. I suppose you can imagine an implementation in which &errno not constant in a certain thread, but in order to use what the footnotes say (set to zero and then check after calling the library function), this should be intentionally adversarial, and perhaps will require specialized compiler support just to be competitive.
In short, if the specification permits erratic &errno , I don't think it was intentional.
[update]
R. asks a wonderful question in the comments. Thinking about it, I believe that now I know the correct answer to his question and the original question. Let me see if I can convince you, dear reader.
R. indicates that GCC allows something similar at the top level:
register int errno asm ("r37");
This will declare errno as the global value stored in register r37 . Obviously, this would be a locally modifiable lvalue value. So, can the corresponding C implementation declare errno as follows?
The answer is no . When you or I use the word "declaration", we usually mean a colloquial and intuitive concept. But the standard does not speak conversationally or intuitively; he speaks for sure, and he aims only at the use of terms that are clearly defined. In the case of an “announcement,” the standard itself defines the term; and when he uses the term, he uses his own definition.
By reading the specification, you can find out exactly what an “announcement” is and what exactly. In other words, the standard describes the language "C". It does not describe "some language that is not C". Regarding the standard, “C with extensions” is simply “some language that is not C”.
Thus, from a standard point of view, the string R is not a declaration at all. He doesn't even make out! He can also read:
long long long __Foo_e!r!r!n!o()blurfl??
Regarding the specification, it is just as “declaration” as the string R; those. not at all.
So, when the C11 specification says, in section 6.5.3.2:
The operand of the unary operator & must be either a function notation, the result of the operator [] or unary * , or lvalue, which indicates an object that is not a bit field and is not declared with the register storage specification.
... that means something very precise that doesn't apply to anything like Line R.
Now consider the declaration of the int object that errno refers to. (Note: I do not mean the declaration of the name errno , since, of course, there can be no such declaration if errno is, say, a macro. I mean the declaration of the base int object.)
The above language says that you can take the lvalue address if it does not indicate a bit field or does not indicate an “declared” register object. And the specification for the base errno object says that it is a modifiable value of int l with local stream duration.
Now it is true that the specification does not mean that the errno base object should be declared at all. Maybe it just appears with the help of a certain compiler magic, defined for implementation. But then again, when the specification says "declared using the register storage class specifier", it uses its own terminology.
Thus, the errno base object is "declared" in the standard sense, in which case it cannot be both register and thread-local; or it is not declared at all, in which case it is not declared register . In any case, since this is an l value, you can accept its address.
(If it is not a bit field, but I think we agree that the bit field is not an object of type int .)