这是Windows和Unix类系统之间一个非常著名的差异。
不管是什么:
- 每个过程有它自己的地址空间,这意味着永远不会在进程间共享的任何内存(除非你使用一些进程间通信库或扩展)。
- 的一个定义规则(ODR)仍然适用,这意味着你只能有全局变量可见在链接时(静态或动态链接)的一个定义。
所以,问题的关键是在这里真的知名度。
在所有情况下,static
全局变量(或函数)在模块外部(dll/so或者可执行文件)永远不可见。 C++标准要求它们具有内部链接,这意味着它们在定义它们的翻译单元(它成为目标文件)之外是不可见的。所以,这解决了这个问题。
它变得复杂的是当你有全局变量extern
。在这里,Windows和类Unix系统完全不同。
对于Windows(.exe和.dll),extern
全局变量不是导出符号的一部分。换句话说,不同的模块根本不知道其他模块中定义的全局变量。这意味着如果您尝试创建一个应该使用DLL中定义的变量的可执行文件(因为这是不允许的),您将会收到链接程序错误。您需要提供一个带有该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全面应用:全局变量将在模块之间共享,这意味着它应该只在加载的所有模块中具有一个定义。
最后,在两种情况下,Windows或Unix类系统中,你可以做运行时联的动态库的,即无论是使用LoadLibrary()
/GetProcAddress()
/FreeLibrary()
或dlopen()
/dlsym()
/dlclose()
。在这种情况下,您必须手动获取您希望使用的每个符号的指针,其中包含您希望使用的全局变量。对于全局变量,只要全局变量是导出符号列表的一部分(按照前面段落的规则),就可以使用GetProcAddress()
或dlsym()
,就像您对函数做的一样。
当然,作为必要的最后说明:应该避免全局变量。我相信你所引用的文本(关于事物“不清楚”)完全是指我刚刚解释的特定于平台的差异(动态库并非真正由C++标准定义,这是特定于平台的领域,意味着它更不可靠/便携)。
*模块*你可能是指* libs *。有一个建议,在C++标准中增加* modules *,并且对于模块的定义和截至目前的常规库有着更为精确的定义。 –
啊,应该澄清一点。我将解决方案中的不同项目(我在Visual Studio中工作很多)视为模块。这些模块内置到* .lib或* .dll中。 – Raja
@DavidRodríguez-dribeas术语“模块”是独立(完全链接的)可执行文件的正确术语,包括:可执行程序,动态链接库(.dll)或共享对象(.so)。这里非常合适,意思是正确的,并且很好理解。正如我所解释的,在有一个名为“模块”的标准功能之前,其定义仍然是传统的功能。 –