这是Windows和类Unix系统之间的一个非常著名的区别。
无论:
- 每个进程都有自己的地址空间,这意味着进程之间永远不会共享任何内存(除非您使用某些进程间通信库或扩展名)。
- “ 单一定义规则”(ODR)仍然适用,这意味着您只能在链接时(静态或动态链接)对全局变量进行一个定义。
因此,这里的关键问题实际上是可见性。
在所有情况下,static
全局变量(或函数)从模块(dll / so或可执行文件)外部永远都不可见。C ++标准要求它们具有内部链接,这意味着它们在定义它们的翻译单元(成为目标文件)之外不可见。因此,解决了这个问题。
当您拥有extern
全局变量时,它变得很复杂。在这里,Windows和类Unix系统完全不同。
对于Windows(.exe和.dll),extern
全局变量不属于导出符号的一部分。换句话说,不同的模块决不会知道其他模块中定义的全局变量。这意味着,例如,尝试创建一个应使用extern
DLL中定义的变量的可执行文件时,会出现链接器错误,因为不允许这样做。您需要提供与外部变量的定义目标文件(或静态库),并用静态链接这两个可执行文件和DLL,从而导致两种不同的全局变量(一个属于可执行文件,一个属于DLL )。
要在Windows中实际导出全局变量,必须使用类似于函数export / import语法的语法,即:
#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 ++标准并未真正定义动态库,这是特定于平台的领域,这意味着它更不可靠/便携式)。