Questa è una differenza piuttosto famosa tra Windows e sistemi simili a Unix.
Non importa cosa:
- Ogni processo ha il proprio spazio degli indirizzi, il che significa che non c'è mai memoria condivisa tra i processi (a meno che non si utilizzi una libreria o estensioni di comunicazione tra processi).
- La One Definition Rule (ODR) si applica ancora, il che significa che puoi avere solo una definizione della variabile globale visibile al momento del collegamento (collegamento statico o dinamico).
Quindi, la questione chiave qui è davvero la visibilità .
In tutti i casi, le static
variabili globali (o funzioni) non sono mai visibili dall'esterno di un modulo (dll / so o eseguibile). Lo standard C ++ richiede che questi abbiano un collegamento interno, il che significa che non sono visibili all'esterno dell'unità di traduzione (che diventa un file oggetto) in cui sono definiti. Quindi, questo risolve il problema.
Dove diventa complicato è quando hai extern
variabili globali. Qui, i sistemi Windows e Unix sono completamente diversi.
Nel caso di Windows (.exe e .dll), le extern
variabili globali non fanno parte dei simboli esportati. In altre parole, moduli diversi non sono in alcun modo consapevoli delle variabili globali definite in altri moduli. Ciò significa che si ottengono errori del linker se si tenta, ad esempio, di creare un eseguibile che dovrebbe utilizzare una extern
variabile definita in una DLL, perché ciò non è consentito. Dovresti fornire un file oggetto (o libreria statica) con una definizione di quella variabile esterna e collegarla staticamente sia con l'eseguibile che con la DLL, risultando in due variabili globali distinte (una appartenente all'eseguibile e una appartenente alla DLL ).
Per esportare effettivamente una variabile globale in Windows, è necessario utilizzare una sintassi simile alla sintassi della funzione di esportazione / importazione, ovvero:
#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif
MY_DLL_EXPORT int my_global;
Quando lo fai, la variabile globale viene aggiunta all'elenco dei simboli esportati e può essere collegata come tutte le altre funzioni.
Nel caso di ambienti tipo Unix (come Linux), le librerie dinamiche, chiamate "oggetti condivisi" con estensione .so
esportano tutte le extern
variabili (o funzioni) globali. In questo caso, se si effettua il collegamento durante il caricamento da qualsiasi luogo a un file oggetto condiviso, le variabili globali vengono condivise, ovvero collegate insieme come una sola. Fondamentalmente, i sistemi simili a Unix sono progettati per fare in modo che non vi sia praticamente alcuna differenza tra il collegamento con una libreria statica o dinamica. Anche in questo caso, ODR si applica su tutta la linea: una extern
variabile globale verrà condivisa tra i moduli, il che significa che dovrebbe avere una sola definizione tra tutti i moduli caricati.
Infine, in entrambi i casi, per sistemi Windows o tipo Unix, è possibile eseguire il collegamento run-time della libreria dinamica, ovvero utilizzando LoadLibrary()
/ GetProcAddress()
/ FreeLibrary()
o dlopen()
/ dlsym()
/ dlclose()
. In tal caso, devi ottenere manualmente un puntatore a ciascuno dei simboli che desideri utilizzare e ciò include le variabili globali che desideri utilizzare. Per le variabili globali, è possibile utilizzare GetProcAddress()
o dlsym()
esattamente come si fa per le funzioni, a condizione che le variabili globali facciano parte dell'elenco di simboli esportato (secondo le regole dei paragrafi precedenti).
E, naturalmente, come nota finale necessaria: le variabili globali dovrebbero essere evitate . E credo che il testo che hai citato (riguardo a cose "poco chiare") si riferisca esattamente alle differenze specifiche della piattaforma che ho appena spiegato (le librerie dinamiche non sono realmente definite dallo standard C ++, questo è un territorio specifico della piattaforma, il che significa è molto meno affidabile / portatile).