È possibile che la sintassi di collegamento standardizzata ANSI C provenga dal primo errore C? [chiuso]

-2

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)

    
posta FISOCPP 24.01.2016 - 17:05
fonte

1 risposta

2

Per prima cosa: l'originale K & R è stato scritto nel 1978. Aspettarsi che un libro scritto nel 1978 sia autorevole ora è una brutta aspettativa.


Il linguaggio C era una propaggine di B, che a sua volta era una propaggine di BCPL. In B, la parola chiave auto era essenzialmente l'opposto della parola chiave extrn . Questo è forse il modo migliore per pensare alla moderna parola chiave% C% co_de quando utilizzata in ambito di blocco.

Per questa risposta più eccellente su stackoverflow.com , lo scopo principale della parola chiave extern al giorno d'oggi è dichiarare un veicolo:

auto vehicle_type my_car;

Un'auto che hai appena acquistato e che devi registrare con il dipartimento dei veicoli a motore:

register vehicle_type my_brand_new_car;

La tua jalopy del 1978:

static vehicle_type my_very_old_car;

Un'auto che non ti appartiene, che è stata registrata con il DMV, e che è un po 'moderna:

extern vehicle_type taxicab;

Infine, ci sono dichiarazioni illegali contro le quali il compilatore deve presentare un reclamo:

extern static register auto vehicle_type car_flooded_by_a_hurricane;

Lingua fuori dalla guancia, la parola chiave auto ha significati diversi nell'ambito del blocco rispetto all'ambito del file. Quello non dovrebbe essere sorprendente; la parola chiave extern ha anche significati diversi nell'ambito del blocco rispetto all'ambito del file. In entrambi i casi, il contesto (ambito del blocco rispetto all'ambito del file) è chiaro, quindi il compilatore sa come interpretare tali parole chiave. (E presumibilmente, così fa l'autore / lettore umano del codice in questione.)

Nell'ambito del file, qualificare alcune dichiarazioni con la parola chiave static significa che la cosa dichiarata è definita in qualche altra unità di compilazione. Risolvere questo è un problema per il linker da risolvere, al contrario del compilatore. Nell'ambito del blocco, la parola chiave extern si riferisce invece a qualcosa dichiarato a un livello superiore oa livello di programma se non esiste una dichiarazione di livello superiore. Se il compilatore può risolvere il riferimento, lo farà. Se non può, rimanda il problema di risolvere il riferimento al linker.


Aggiornamento: dettagli sull'avvocato linguistico

Le parole chiave extern , auto , extern , register e static sono specificatori della classe di memoria . Un simbolo può essere qualificato al massimo da un identificatore della classe di memoria . La dichiarazione typedef non ha senso (per non parlare di extern static int foo ), e questo non è ciò che K & R intende per una variabile statica esterna .

K & R (Kernighan e Ritchie) è rivolto ai normali lettori umani. Quando hanno scritto su variabile statica esterna (carattere non di codice), intendevano una variabile che non è automatica, non è un registro e non è visibile al linker. Non intendevano extern static register auto int foo (carattere del codice), figuriamoci il mio extern static . Ciò non aveva senso anche nel 1978, per non parlare del 2016.

Quale K & R inteso da una "variabile statica esternamente" è una variabile dichiarata a livello di blocco qualificato con la parola chiave extern static register auto vehicle_type car_flooded_by_a_hurricane che era stata precedentemente dichiarata nell'ambito del file come extern . Non c'è stato errore nel 1978, e non c'è errore quasi 40 anni dopo.

    
risposta data 24.01.2016 - 19:46
fonte

Leggi altre domande sui tag