这是Windows和类Unix系统之间一个相当著名的区别。
不管是什么。
Each
process
has its own address space, meaning that there is never any memory being shared between processes (unless you use some inter-process communication library or extensions).
The
One Definition Rule
(ODR) still applies, meaning that you can only have one definition of the global variable visible at link-time (static or dynamic linking).
因此,这里的关键问题实际上是
visibility
.
在所有情况下,
static
全局变量(或函数)在模块(dll/so或可执行文件)之外是不可见的。C++标准要求这些变量具有内部链接性,这意味着它们在定义它们的翻译单元(成为一个对象文件)之外是不可见的。所以,这就解决了这个问题。
当你有
extern
全局变量时,情况就变得复杂了。在这里,Windows和类Unix系统是完全不同的。
在Windows(.exe和.dll)的情况下,
extern
全局变量不属于导出的符号。换句话说,不同的模块根本不知道其他模块中定义的全局变量。这意味着,如果你试图创建一个应该使用定义在DLL中的
extern
变量的可执行文件,你会得到链接器错误,因为这是不允许的。你需要提供一个带有该外部变量定义的对象文件(或静态库),并将其静态链接到
both
可执行文件和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的系统,你可以做到run-time动态库的链接,即使用LoadLibrary()
或/ 【替换代码9/ FreeLibrary()
或dlopen()
。/ 【替换代码12/ dlclose()
。在这种情况下,你必须手动获取你想使用的每个符号的指针,这包括你想使用的全局变量。对于全局变量,你可以像对待函数一样使用GetProcAddress()
或dlsym()
,只要全局变量是导出的符号列表的一部分(根据前面几段的规则)。
当然,作为一个必要的最后说明。应避免使用全局变量而且我相信你引用的那段文字(关于事情 "不清楚")正是指我刚才解释的平台特有的差异(动态库并不是由C++标准真正定义的,这是平台特有的领域,意味着它的可靠性/可移植性要差很多)。