In che modo i compilatori c ++ trovano una variabile esterna?


15

Compilo questo programma con g ++ e clang ++. C'è una differenza:
g ++ stampa 1, ma clang ++ stampa 2.
Sembra che
g ++: la variabile esterna sia definita nell'ambito più breve.
clang ++: la variabile extern è definita nell'ambito globale più breve.

La specifica C ++ ha qualche specifica al riguardo?

main.cpp

#include <iostream>
static int i;
static int *p = &i;

int main() {
  int i;
  {
    extern int i;
    i = 1;
    *p = 2;
    std::cout << i << std::endl;
  }
}

other.cpp

int i;

versione: g ++: 7.4.0 / clang ++:
compilazione 10.0.0 : $ (CXX) main.cpp other.cpp -o extern.exe


4
Il compilatore non fa nulla con extern tranne che contrassegnarli come variabili che hanno riferimenti esterni, il linker è ciò che tenta di risolvere i collegamenti tra tutti i file degli oggetti compilati.
SPlatten,

Un'ottima (se strana) domanda! Giocando con il tuo codice MSVCe clang-cl(entrambi danno 2), sembra che extern int isia completamente ignorato da entrambi: anche se non mi collego al other.cppfile, il programma viene compilato ed eseguito.
Adrian Mole il

1
@SPlatten Presumibilmente, poiché il linker non ha bisogno di "risolvere" il riferimento i, non ci prova.
Adrian Mole

3
Il vecchio bug GCC sospeso correlato può essere trovato qui e il corrispondente bug Clang aperto qui
noce

Risposte:


11

[basic.link/7] dovrebbe essere la parte pertinente della norma. Nella bozza attuale, dice:

Il nome di una funzione dichiarata nell'ambito del blocco e il nome di una variabile dichiarata da una externdichiarazione dell'ambito del blocco hanno un collegamento. Se una tale dichiarazione è allegata a un modulo denominato, il programma è mal formato. Se è presente una dichiarazione visibile di un'entità con collegamento, che ignora le entità dichiarate al di fuori dell'ambito dello spazio dei nomi che racchiude più interno, tale che la dichiarazione dell'ambito del blocco sarebbe una dichiarazione (possibilmente mal formata) se le due dichiarazioni apparissero nella stessa regione dichiarativa, il la dichiarazione dell'ambito del blocco dichiara quella stessa entità e riceve il collegamento della dichiarazione precedente. Se esiste più di un'entità corrispondente, il programma è mal formato. Altrimenti, se non viene trovata alcuna entità corrispondente, l'entità dell'ambito del blocco riceve un collegamento esterno.Se, all'interno di un'unità di traduzione, la stessa entità viene dichiarata sia con collegamento interno che esterno, il programma è mal formato.

Nota che l'esempio successivo corrisponde quasi esattamente al tuo caso:

static void f();
extern "C" void h();
static int i = 0;               // #1
void g() {
  extern void f();              // internal linkage
  extern void h();              // C language linkage
  int i;                        // #2: i has no linkage
  {
    extern void f();            // internal linkage
    extern int i;               // #3: external linkage, ill-formed
  }
}

Quindi, il programma dovrebbe essere mal formato. La spiegazione è sotto l'esempio:

Senza la dichiarazione alla riga n. 2, la dichiarazione alla riga n. 3 si collegherebbe con la dichiarazione alla riga n. 1. Poiché la dichiarazione con collegamento interno è nascosta, tuttavia, al n. 3 viene assegnato un collegamento esterno, rendendo il programma mal formato.


Il programma nell'esempio è mal formato perché non esiste alcun io con collegamento esterno definito da nessuna parte. Questo non è il caso dell'esempio di OP.
n. 'pronomi' m.

3
@ n.'pronouns'm. Ma la regola si applica a un'unità di traduzione: se, all'interno di un'unità di traduzione, la stessa entità viene dichiarata con un collegamento sia interno che esterno, il programma è mal formato. .
Daniel Langr

2
La risposta si applica solo a C ++ 17 e versioni successive, vedere la risoluzione del problema 426 di CWG . Mi sembra che GCC fosse corretto prima di quel cambiamento.
noce

OK, sembra che stavo leggendo la precedente edizione dello standard.
n. 'pronomi' m.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.