全局变量在Windows上有多个副本,在exec和共享libaray中编译时在Linux上有一个副本

问题描述 投票:6回答:5

*修订问题(见下文)*

我有一个cpp文件,它定义了一个静态全局变量,例如

static Foo bar;

此cpp文件被编译为可执行文件和共享库。可执行文件可以在运行时加载共享库。

如果我在Linux上,似乎有两个这个变量的副本。我假设一个来自可执行文件,另一个来自共享库。其他平台(惠普,Windows)似乎只有一个副本。

什么控制Linux上的这种行为,我可以改变它吗?例如,是否存在编译器或链接器标志,它会强制共享库中此变量的版本与可执行文件中的变量相同?

*问题的修订*

谢谢你到目前为止的答案。在重新审视问题时,实际上并不是上述问题。上面的静态全局变量确实在Windows上有多个副本,因此与我在Linux上看到的没有区别。

但是,我有另一个全局变量(这次不是静态的),它在cpp文件中声明,在头文件中声明为extern。

在Windows上,这个变量有多个副本,一个在可执行文件中,一个在每个dll中加载,而在Linux上它只有一个。所以现在的问题是关于这种差异。如何让Linux拥有多个副本?

(我的程序的逻辑意味着静态全局变量的值取决于非静态全局变量的值,我开始指责错误的变量是问题)

c++ linux
5个回答
4
投票

我强烈建议你阅读following。之后,您将了解Linux中共享库的所有内容。正如其他人所说,快速回答是static关键字将全局变量的范围限制为翻译单元(因此也限制为可执行文件或共享库)。在标题中使用关键字extern,并仅在其中一个模块(exe或dll / so)中编译包含相同全局变量的cpp将使全局变量唯一并在所有模块之间共享。

编辑:当你使用extern模式时,Windows上的行为与Linux上的行为不同,因为Windows加载动态链接库(dll)的方法不一样,并且基本上无法动态链接全局变量(这样只存在一个)。如果您可以使用DLL的静态加载(不使用LoadLibrary),那么您可以使用以下内容:

//In one module which has the actual global variable:
__declspec(dllexport) myClass myGlobalObject;
//In all other modules:
__declspec(dllimport) myClass myGlobalObject;

这将使myGlobalObject在使用DLL的所有模块中独一无二并共享,其中使用了上述第一版的DLL。

如果您希望每个模块都有自己的全局变量实例,那么使用static关键字,Linux或Windows的行为将是相同的。

如果你想要一个全局变量的唯一实例并且需要动态加载(LoadLibrarydlopen),你必须创建一个初始化函数来为每个加载的DLL提供一个指向全局变量的指针(在使用它之前)。您还必须保留引用计数(您可以使用shared_ptr),这样您就可以在不存在时创建一个新的,否则增加计数,并且当计数变为零时能够删除它,因为DLL是被卸下。


3
投票

应用于命名空间变量的static限定符意味着变量的范围是转换单元。这意味着如果在标头中定义了该变量,并且您从多个.cpp文件中包含该变量,那么您将获得每个变量的副本。如果你想要一个副本,那么在标题中将其标记为extern(而不是static),在单个翻译单元中定义它并将其链接到可执行文件或库中(但不是两者)。


1
投票

你在每个平台上使用了什么编译器?您为Linux描述的行为将是我所期望的,静态全局仅在编译时对该特定文件是本地的。


1
投票

您可以使用GCC visibility属性或visibility pragma解决您的问题


0
投票

我不知道HPUX,但在Windows上,如果你有一个exe和一个DLL,并且每个都声明了全局变量,那么将会有两个不同的变量。如果您只获得单个变量,则一个图像必须从另一个图像导入变量。

© www.soinside.com 2019 - 2024. All rights reserved.