C'est une différence assez connue entre les systèmes Windows et les systèmes de type Unix.
Peu importe :
- Chaque processus a son propre espace d'adressage, ce qui signifie qu'il n'y a jamais de mémoire partagée entre les processus (à moins d'utiliser une bibliothèque de communication inter-processus ou des extensions).
- La Règle de la Définition Unique (ODR) s'applique toujours, ce qui signifie que vous ne pouvez avoir qu'une seule définition de la variable globale visible au moment du lien (liaison statique ou dynamique).
Donc, la question clé ici est vraiment la visibilité.
Dans tous les cas, les variables globales static
(ou fonctions) ne sont jamais visibles depuis l'extérieur d'un module (dll/so ou exécutable). La norme C++ exige que celles-ci aient une liaison interne, ce qui signifie qu'elles ne sont pas visibles en dehors de l'unité de traduction (qui devient un fichier objet) dans laquelle elles sont définies. Donc, cela règle cette question.
Les choses se compliquent lorsque vous avez des variables globales extern
. Ici, les systèmes Windows et de type Unix sont complètement différents.
Dans le cas de Windows (.exe et .dll), les variables globales extern
ne font pas partie des symboles exportés. En d'autres termes, les différents modules ne sont en aucun cas conscients des variables globales définies dans d'autres modules. Cela signifie que vous obtiendrez des erreurs de lien si vous essayez, par exemple, de créer un exécutable censé utiliser une variable extern
définie dans une DLL, car cela n'est pas autorisé. Vous devriez fournir un fichier objet (ou une bibliothèque statique) avec une définition de cette variable externe et la lier statiquement avec à la fois l'exécutable et la DLL, ce qui se traduit par deux variables globales distinctes (une appartenant à l'exécutable et une appartenant à la DLL).
Pour exporter réellement une variable globale sous Windows, vous devez utiliser une syntaxe similaire à la syntaxe d'exportation/importation de fonction, c'est-à-dire :
#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;
Lorsque vous faites cela, la variable globale est ajoutée à la liste des symboles exportés et peut être liée comme toutes les autres fonctions.
Dans le cas des environnements de type Unix (comme Linux), les bibliothèques dynamiques, appelées "objets partagés" avec l'extension .so
, exportent toutes les variables globales extern
(ou fonctions). Dans ce cas, si vous faites un lien au moment du chargement à partir de n'importe quel endroit vers un fichier objet partagé, alors les variables globales sont partagées, c'est-à-dire, liées ensemble comme une seule. Fondamentalement, les systèmes de type Unix sont conçus de manière à ce qu'il n'y ait pratiquement aucune différence entre le lien avec une bibliothèque statique ou dynamique. Encore une fois, la règle de la Définition Unique s'applique de manière générale : une variable globale extern
sera partagée entre les modules, ce qui signifie qu'elle ne devrait avoir qu'une seule définition à travers tous les modules chargés.
Enfin, dans les deux cas, pour Windows ou les systèmes de type Unix, vous pouvez effectuer un lien au moment de l'exécution de la bibliothèque dynamique, c'est-à-dire en utilisant soit LoadLibrary()
/GetProcAddress()
/FreeLibrary()
ou dlopen()
/dlsym()
/dlclose()
. Dans ce cas, vous devez obtenir manuellement un pointeur vers chacun des symboles que vous souhaitez utiliser, et cela inclut les variables globales que vous souhaitez utiliser. Pour les variables globales, vous pouvez utiliser GetProcAddress()
ou dlsym()
de la même manière que vous le faites pour les fonctions, à condition que les variables globales fassent partie de la liste des symboles exportés (selon les règles des paragraphes précédents).
Et bien sûr, en tant que note finale nécessaire : les variables globales doivent être évitées. Et je crois que le texte que vous avez cité (à propos des choses étant "peu claires") fait référence exactement aux différences spécifiques à la plate-forme que je viens d'expliquer (les bibliothèques dynamiques ne sont pas vraiment définies par la norme C++, c'est un territoire spécifique à la plateforme, ce qui signifie que c'est beaucoup moins fiable/portable).