Recentemente mi sono avvicinato a qualcosa di illogico, leggendo l'ultimo documento ANSI C. Stava parlando di linkage ma non ha mai menzionato un modo per dichiarare identificatori interni all'interno di un ambito di blocco (o almeno in un modo utile). Immagina qualcosa del genere:
int main()
{
//Here how to declare the identifier 'b'
//with internal linkage
b = 2;
printf("%d", b);
}
static int b;
Se provi qualcosa del genere:
{
extern int b;
//...
}
Quindi questo sarebbe UB perché l'identificatore b
è dichiarato sia interno che esterno nella stessa TU (secondo lo standard corrente).
Tuttavia, leggendo il white paper di K & B ("The C Programming Language Edition 1") mi sono avvicinato a questo (a pagina 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.
Vedi la parte importante è che in questo esempio (che mostra come un compilatore può essere scritto) si considera la possibilità per un identificatore di avere sia static
che extern
identificatore di memoria (ricordarsi nell'attuale standard ANSI C possiamo avere solo un singolo identificatore di memoria). Che in realtà ha molto senso. In questo modo abbiamo potuto specificare in una dichiarazione qualunque cosa ci riferiamo all'identificatore con collegamento interno o esterno. Questo sarebbe utile in alcune situazioni. Immagina di avere una variabile interna ed esterna con lo stesso identificatore. In questo modo abbiamo potuto distinguere tra entrambi:
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
Nell'attuale standard ANSI C questo non è supportato e c'è anche qualche regola sintetica che se un identificatore è dichiarato con l'identificatore extern
e c'è già tale identificatore visibile con qualche linkage - questo collegamento è ereditato dall'identificatore attualmente dichiarato.
Questo porta a codice come questo:
static int b;
int main()
{
extern int b; //re-declaration of identifier 'b' with internal linkage
b = 2;
}
Che è piuttosto confuso, almeno. Scrivendo tale codice supporrà che l'identificatore con collegamenti esterni sia stato modificato.
Tieni presente che il solo inserimento di static
in una dichiarazione a livello di blocco modifica solo il tipo di archiviazione della variabile.
Ecco le dichiarazioni ANSI C originali a riguardo:
$ 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.
E a $ 6.2.2.7:
If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined.
Informazioni sugli identificatori di spazio di archiviazione a $ 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)