我试图了解将具有全局变量和静态变量的模块动态链接到应用程序时会发生什么。
所谓模块,是指解决方案中的每个项目(我在Visual Studio中工作很多!)。这些模块内置于* .lib或* .dll或* .exe本身中。
我了解到,应用程序的二进制文件包含数据段中所有单个转换单元(目标文件)的全局和静态数据(如果为const,则为只读数据段)。
(请同时说明您的原因)
引用MSDN:
并从here:
谢谢。
最佳答案
这是Windows和类Unix系统之间的一个非常著名的区别。
无论:
因此,这里的关键问题实际上是可见性。
在所有情况下,
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++标准并未真正定义动态库,这是特定于平台的领域,这意味着它更不可靠/便携式)。
关于c++ - 动态链接共享库时,全局变量和静态变量会发生什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62156829/