C89, 4.1.2 Standard headers (emphasis added):
All external identifiers that begin with an underscore are reserved.
C99 (and later), 7.1.3 Reserved identifiers, 1 (emphasis added):
All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.
Rationale for C, 7.1.3 Reserved identifiers, 25 (emphasis added):
Also reserved for the implementor are all external identifiers beginning with an underscore, and all other identifiers beginning with an underscore followed by a capital letter or an underscore.
So, per C99 (and later), in:
typedef int _t; // non-reserved in C89, reserved in C99 (and later)
static int _f(int x); // non-reserved in C89, reserved in C99 (and later)
(declared at file scope) the _t
and _f
are reserved, which contradicts with Rationale and with C89.
Does it mean that C99 (and later) misses "external" in 7.1.3 Reserved identifiers: "All external identifiers that ..."?
The table:
is _x reserved ?
C89 C99 (and later)
scope - linkage
function - external n/a n/a
function - internal n/a n/a
function - none no no
file - external yes yes**
file - internal no yes**
file - none no yes**
block - external yes no*
block - internal n/a n/a
block - none no no
function prototype - external n/a n/a
function prototype - internal n/a n/a
function prototype - none no no
where *
is a possible defect, see below and **
is "in both the ordinary and tag name spaces" (C11, 7.1.3 Reserved identifiers, 1).
Here we see that C99 (and later) reserves more (if the table is correct). Extra: for what purpose?
Does C99 (and later) 7.1.3 Reserved identifiers miss "external"?
Clearly C99 and each subsequent version of C, up to and including the current latest, C17, omit the qualification "external" in the provision you're asking about. That doesn't necessarily constitute a defect in their text.
So, per C99 (and later), in:
typedef int _t; // non-reserved in C89, reserved in C99 (and later) static int _f(int x); // non-reserved in C89, reserved in C99 (and later)
(declared at file scope) the
_t
and_f
are reserved, which contradicts with Rationale and with C89.
Yes, those declarations, appearing at file scope, declare _t
and _f
with no linkage and internal linkage, respectively. They are not external identifiers (though they are declared in external declarations), and therefore C89 does not reserve them for that use. C99 and later, on the other hand, do reserve them for that use.
"Contradicts" is too strong for this difference. Yes, C99 reserves some identifier usages that C89 does not (and vice versa). This is not the only backwards incompatibility between the two. C99 "cancels and replaces" C89, so the former is not required to be consistent with all details of the latter.
As for the rationale, yes, it explains the C89 version of the reservations, not the C99 form. I take that to be a flaw in the rationale, which, after all, is not normative.
Does it mean that C99 (and later) misses "external" in 7.1.3 Reserved identifiers: "All external identifiers that ..."?
I see no reason to conclude that the omission of "external" from that provision in C99 and later represents an editorial error. In fact, I take corresponding changes in the surrounding text as evidence that the change was intentional. In particular, The C89 text continues with "all other identifiers that begin with an underscore and either an upper-case letter or another underscore" (emphasis added), whereas C99 and later drop the "other" as well. As far as I can see, C99 intended to reserve more identifier uses than C89 did.
But there is a possible defect here in that C89 reserves some identifier uses that C99 does not, but might have intended to do. These are external identifiers at block scope that start with an underscore not immediately followed by a capital letter or another underscore. Example:
int f(void) {
extern int _var;
extern int _func(int);
// ...
}
Those uses of _var
and _func
are reserved in C89, but not in C99 and later. Conforming C code cannot provide definitions to link to those identifiers, as the identifier reservations in all versions of the spec disallow that, but they could collide with external identifiers defined by the implementation.