Recently I came up to something illogical, reading the latest ANSI C paper. It was talking about linkage but it never mentioned a way to declare internal identifiers inside block-scope (or at least in a useful way). Imagine something like this:
int main()
{
//Here how to declare the identifier 'b'
//with internal linkage
b = 2;
printf("%d", b);
}
static int b;
If you try something like this:
{
extern int b;
//...
}
Then the this would be UB because the identifier b
is declared both internal and external in the same TU (according to the current standard).
However reading the K&B white paper ("The C Programming Language Edition 1") I came up to this (at page 137):
Imagine a fragment of a compiler that manipulates a symbol table. Each identifier in a program has certain information associated with it, for example, whether or not it is a keyword, whether or not it is external and/or static, and so on. The most compact way to encode such information is a set of one-bit flags in a single char or int.
See the important part is that in this example (which shows how a compiler could be written) it is considered the possibility for an identifier to have both static
and extern
storage specifier (remember in the current ANSI C standard we can only have a single storage specifier). Which actually make a lot of sense. This way we could specify in a declaration whatever we are referring to the identifier with internal or external linkage. This would be useful in certain situations. Imagine we have internal and external variable with the same identifier. This way we could distinguish between the both:
int main()
{
extern static int b; //here internal variable should be declared (!)
b = 2; //here internal variable should be modified
{
extern int b; //here external variable should be declared (!)
b = 2; //here external variable should be modified
}
}
static int b; //internal variable named 'b' defined here
In the current ANSI C standard this is not supported and also there is some synthetic rule that if an identifier is declared with the extern
specifier and there is already such identifier visible with some linkage — this linkage is inherit to the identifier currently declared.
This lead to code like this:
static int b;
int main()
{
extern int b; //re-declaration of identifier 'b' with internal linkage
b = 2;
}
Which is rather confusing at least. Writing such code you'll suppose that the identifier with external linkages is modified.
Note that just putting static
in a block-scope declarations only alters the storage-type of the variable.
Here are the original ANSI C statements about this:
$6.2.2.4:
For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible,31) if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.
And at $6.2.2.7:
If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.
About storage-specifiers at $6.7.1.2:
At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern)