静态全局变量的奇怪行为

Strange behavior of static global variable

我知道这个程序没有以适当的方式使用静态变量,但它展示了如何重现我所看到的行为:

Main.cpp :

int main(){
    MyObject* p = new MyObject();
    Header::i = 5;

    printf("i %i\n", Header::i);
    p->update();

    return 0;
}

MyObject.cpp :

MyObject::MyObject(){
}

void MyObject::update(){
    printf("i %i\n", Header::i);
}

Extern.h :

namespace Header {
    static int i;
};

我得到的输出是:

i : 5
i : 0

为什么我的两个输出都没有得到 5?这个 0 来自哪里? 你能解释一下静态变量是如何工作的吗?

静态变量具有内部链接,这实际上意味着它们对于编译单元而言是本地的。由于您在 2 个源文件中的 header 中声明了静态变量,因此您基本上有 2 个不同的变量:一个 i 局部于 MyObject.cpp 和另一个不同的 imain.cpp

本地

你有两个变量 i

static int i;

因为它有内部链接。这意味着包含相应 header 的每个编译单元都有自己的 object i 并且其他编译单元不知道该编译单元中 object 的存在。

如果您要删除说明符 static,那么链接器应该发出一条消息,指出该变量被定义了两次。

如果在 C++ 2011 中将变量放在未命名的命名空间中,也可以达到相同的效果。例如,而不是

namespace Header {
    static int i;
};

你可以写

namespace {
    int i;
};

在这种情况下,变量 i 也有内部链接。这对 C++ 2011 有效。

声明为 static 的变量仅在声明它的文件中有范围,而声明为 static 的变量可以使用 extern 声明从其他文件访问。

您将 class 级静态变量与命名空间级静态变量混淆了。两者都通过 X::y 资格访问,增加了混乱。其他人已经解释了实际原因(在 compilation/linkage 级别)。

每个包含 header 的翻译单元都有一个静态变量,因为静态变量具有内部链接。

Where does this 0 come from ?

你没有初始化第二个翻译单元中的变量,静态变量是zero-initialized,这就是0的来源。

在标准 (§3.6.2/2) 中:

Variables with static storage duration (3.7.1) [...] shall be zero-initialized (8.5) before any other initialization takes place.[...]

最好在头文件中用 extern 声明变量,以指定它具有外部 linkage。否则会发生上述行为或潜在的编译或 link 问题。

static int i ; // i has internal linkage
extern int i ; // i has external linkage

您不应将静态变量放入 header 文件中。这导致每个包含 header 的 cpp 文件都将其静态本地副本复制到其编译单元。

你可以做的是外部存储说明符:

Header:

namespace Header {
    extern int i;
}

Cpp:

namespace Header {
    int i = 0;
}

作为对所有答案的补充。为什么会发生,已经解释过了。但是,到目前为止,仅建议使用 static/extern 方法来修复它。这个有点C-like。除非你不必使用 C-linkage 项目的 C-part 中的 header,否则你可以使用 C++。

所以 IF 你真的必须在你的代码中使用一些静态的东西。

要么将变量声明为class:

的成员

header.h

MyGlobalVariableHoler
{
  public: static int i;
};

main.cpp

// class' statics have has to be initialized, otherwise linker error.
int MyGlobalVariableHoler::i=0;

any_code.cpp

#include <header.h>

MyGlobalVariableHolder::i=4711;

或者使用单例来避免显式初始化

header.h

MyGlobalVariableHolder
{
    MyGlobalVariableHolder(){i=0;}
  public:
    static MyGlobalVariableHolder & instance()
    {
       static MyGlobalVariableHolder inst;
       return inst;
    }
    int i;
};

any_code.cpp

#include <header.h>
MyGlobalVariableHolder::instance().i=4711;