这是Windows和类Unix系统之间非常著名的区别。
无论如何:
- 每个进程都有自己的地址空间,这意味着永远不会在进程之间共享内存(除非您使用一些进程间通信库或扩展)。
- 仍然适用单一定义规则(ODR),这意味着您只能在链接时看到一个全局变量的定义(静态或动态链接)。
所以,关键问题实际上是可见性。
在所有情况下,static
全局变量(或函数)从未从模块(dll / so或可执行文件)外部可见。 C ++标准要求这些具有内部链接,这意味着它们在定义它们的翻译单位(成为对象文件)之外不可见。所以,这解决了那个问题。
当存在extern
全局变量时,情况变得复杂起来。这里,Windows和类Unix系统完全不同。
在Windows(.exe 和 .dll)的情况下,extern
全局变量不属于导出符号的一部分。换句话说,不同模块无法意识到其他模块中定义的全局变量。这意味着,如果您尝试创建一个应该使用在DLL中定义的extern
变量的可执行文件,则会获得链接器错误,因为这是不允许的。您需要提供一个带有该extern变量的定义的对象文件(或静态库),并将其静态链接到两个可执行文件和DLL,从而产生两个不同的全局变量(一个属于可执行文件,一个属于 DLL)。
要在Windows中实际导出全局变量,您必须使用类似于函数导出/导入语法的语法,例如:
#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
当你这样做时,全局变量将被添加到导出符号列表中,可以像所有其他函数一样链接。
在类Unix环境(如Linux)的情况下,称为“共享对象”的动态库使用扩展名为
.so
,导出所有
extern
全局变量(或函数)。 在这种情况下,如果您从任何地方对共享对象文件进行
加载时间链接,则全局变量是共享的,即作为一个链接在一起。 基本上,类Unix系统被设计成在静态或动态库链接之间几乎没有区别。 再次强调ODR适用于各种情况:一个
extern
全局变量将在模块间共享,这意味着它应该在加载的所有模块中具有唯一的定义。
最后,在Windows或类Unix系统的两种情况下,您可以对动态库进行
运行时链接,即使用
LoadLibrary()
/
GetProcAddress()
/
FreeLibrary()
或
dlopen()
/
dlsym()
/
dlclose()
。 在这种情况下,您必须手动获取指向想要使用的每个符号的指针,其中包括您想要使用的全局变量。 对于全局变量,您可以像处理函数一样使用
GetProcAddress()
或
dlsym()
,前提是全局变量是导出符号列表的一部分(根据上面的规则)。
当然,作为必要的最后说明:
应该避免使用全局变量。 我认为您引用的文本(关于事情“不清楚”)恰好指的是我刚刚解释的平台特定差异(动态库实际上并没有由C ++标准定义,这是平台特定的领域,这意味着它的可靠性/可移植性要低得多)。