Essa é uma diferença bastante famosa entre os sistemas Windows e Unix.
Não importa o que:
- Cada processo possui seu próprio espaço de endereço, o que significa que nunca há nenhuma memória sendo compartilhada entre os processos (a menos que você use alguma biblioteca ou extensões de comunicação entre processos).
- A Regra de Uma Definição (ODR) ainda se aplica, o que significa que você só pode ter uma definição da variável global visível no momento do link (vinculação estática ou dinâmica).
Portanto, a questão principal aqui é realmente a visibilidade .
Em todos os casos, static
variáveis globais (ou funções) nunca são visíveis de fora de um módulo (dll / so ou executável). O padrão C ++ exige que eles tenham ligação interna, o que significa que não são visíveis fora da unidade de tradução (que se torna um arquivo de objeto) na qual estão definidos. Então, isso resolve esse problema.
Onde fica complicado é quando você tem extern
variáveis globais. Aqui, os sistemas Windows e Unix são completamente diferentes.
No caso do Windows (.exe e .dll), as extern
variáveis globais não fazem parte dos símbolos exportados. Em outras palavras, módulos diferentes não estão cientes das variáveis globais definidas em outros módulos. Isso significa que você receberá erros do vinculador se tentar, por exemplo, criar um executável que deve usar uma extern
variável definida em uma DLL, porque isso não é permitido. Você precisaria fornecer um arquivo de objeto (ou biblioteca estática) com uma definição dessa variável externa e vinculá-la estaticamente a ambos o executável ea DLL, resultando em duas variáveis distintas globais (um pertencente ao executável e um pertencentes à DLL )
Para realmente exportar uma variável global no Windows, você deve usar uma sintaxe semelhante à função exportar / importar sintaxe, ou seja:
#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 você faz isso, a variável global é adicionada à lista de símbolos exportados e pode ser vinculada como todas as outras funções.
No caso de ambientes do tipo Unix (como Linux), as bibliotecas dinâmicas, chamadas "objetos compartilhados" com extensão, .so
exportam todas extern
as variáveis (ou funções) globais. Nesse caso, se você vincular o tempo de carregamento de qualquer lugar a um arquivo de objeto compartilhado, as variáveis globais serão compartilhadas, ou seja, vinculadas como um. Basicamente, sistemas do tipo Unix são projetados para torná-lo de forma que não haja virtualmente nenhuma diferença entre vincular-se a uma biblioteca estática ou dinâmica. Novamente, o ODR se aplica de maneira geral: uma extern
variável global será compartilhada entre os módulos, o que significa que ela deve ter apenas uma definição em todos os módulos carregados.
Finalmente, nos dois casos, para sistemas Windows ou Unix, você pode vincular a biblioteca dinâmica em tempo de execução , ou seja, usando LoadLibrary()
/ GetProcAddress()
/ FreeLibrary()
ou dlopen()
/ dlsym()
/ dlclose()
. Nesse caso, você precisa obter manualmente um ponteiro para cada um dos símbolos que deseja usar, e isso inclui as variáveis globais que você deseja usar. Para variáveis globais, você pode usar GetProcAddress()
ou dlsym()
da mesma forma que para funções, desde que as variáveis globais façam parte da lista de símbolos exportados (pelas regras dos parágrafos anteriores).
E, claro, como uma nota final necessária: variáveis globais devem ser evitadas . E acredito que o texto que você citou (sobre coisas que não são claras) se refere exatamente às diferenças específicas da plataforma que acabei de explicar (as bibliotecas dinâmicas não são realmente definidas pelo padrão C ++, este é um território específico da plataforma, ou seja, é muito menos confiável / portátil).