C++ 何时分配/初始化函数级静态变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/55510/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 12:30:53  来源:igfitidea点击:

When do function-level static variables get allocated/initialized?

c++variables

提问by Owen

I'm quite confident that globally declared variables get allocated (and initialized, if applicable) at program start time.

我非常有信心全局声明的变量在程序启动时被分配(并初始化,如果适用)。

int globalgarbage;
unsigned int anumber = 42;

But what about static ones defined within a function?

但是在函数中定义的静态函数呢?

void doSomething()
{
  static bool globalish = true;
  // ...
}

When is the space for globalishallocated? I'm guessing when the program starts. But does it get initialized then too? Or is it initialized when doSomething()is first called?

什么时候globalish分配空间?我猜程序什么时候开始。但是它也被初始化了吗?还是在doSomething()第一次调用时初始化?

回答by Adam Pierce

I was curious about this so I wrote the following test program and compiled it with g++ version 4.1.2.

我对这个很好奇所以我写了下面的测试程序并用g++ 4.1.2版编译它。

include <iostream>
#include <string>

using namespace std;

class test
{
public:
        test(const char *name)
                : _name(name)
        {
                cout << _name << " created" << endl;
        }

        ~test()
        {
                cout << _name << " destroyed" << endl;
        }

        string _name;
};

test t("global variable");

void f()
{
        static test t("static variable");

        test t2("Local variable");

        cout << "Function executed" << endl;
}


int main()
{
        test t("local to main");

        cout << "Program start" << endl;

        f();

        cout << "Program end" << endl;
        return 0;
}

The results were not what I expected. The constructor for the static object was not called until the first time the function was called. Here is the output:

结果出乎我的意料。直到第一次调用该函数时,才调用静态对象的构造函数。这是输出:

global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed

回答by Owen

Some relevant verbiage from C++ Standard:

C++ 标准中的一些相关措辞:

3.6.2 Initialization of non-local objects [basic.start.init]

1

The storage for objects with static storage duration (basic.stc.static) shall be zero-initialized (dcl.init) before any other initialization takes place. Objects of POD types (basic.types) with static storage duration initialized with constant expressions (expr.const) shall be initialized before any dynamic initialization takes place. Objects of namespace scope with static storage duration defined in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit. [Note: dcl.init.aggrdescribes the order in which aggregate members are initialized. The initialization of local static objects is described in stmt.dcl. ]

[more text below adding more liberties for compiler writers]

6.7 Declaration statement [stmt.dcl]

...

4

The zero-initialization (dcl.init) of all local objects with static storage duration (basic.stc.static) is performed before any other initialization takes place. A local object of POD type (basic.types) with static storage duration initialized with constant-expressions is initialized before its block is first entered. An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope (basic.start.init). Otherwise such an object is initialized the first time control passes through its declaration; such an object is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control re-enters the declaration (recursively) while the object is being initialized, the behavior is undefined. [Example:

      int foo(int i)
      {
          static int s = foo(2*i);  // recursive call - undefined
          return i+1;
      }

--end example]

5

The destructor for a local object with static storage duration will be executed if and only if the variable was constructed. [Note: basic.start.termdescribes the order in which local objects with static storage duration are destroyed. ]

3.6.2 非本地对象的初始化【basic.start.init】

1

具有静态存储持续时间 ( basic.stc.static) 的对象的存储应在任何其他初始化发生之前进行零初始化 ( dcl.init)。使用常量表达式 ( expr.const)初始化静态存储持续时间的 POD 类型 ( basic.types)对象应在任何动态初始化发生之前进行初始化。在同一个翻译单元中定义并动态初始化的具有静态存储期限的命名空间范围的对象应按照其定义在翻译单元中出现的顺序进行初始化。[注: dcl.init.aggr描述聚合成员初始化的顺序。stmt.dcl 中描述了本地静态对象的初始化。]

[下面的更多文字为编译器编写者增加了更多的自由]

6.7 声明语句[stmt.dcl]

...

4

所有具有静态存储持续时间 ( basic.stc.static) 的本地对象的零初始化 ( dcl.init)在任何其他初始化发生之前执行。具有用常量表达式初始化的静态存储持续时间的 POD 类型 ( basic.types) 的本地对象在其块首次进入之前被初始化。在允许实现在命名空间范围内静态初始化具有静态存储持续时间的对象(basic.start.init)。否则,这样的对象在第一次控制通过其声明时被初始化;这样的对象在其初始化完成后被视为已初始化。如果初始化通过抛出异常退出,则初始化未完成,因此将在下次控件进入声明时再次尝试。如果在初始化对象时控制重新进入声明(递归),则行为未定义。[示例:

      int foo(int i)
      {
          static int s = foo(2*i);  // recursive call - undefined
          return i+1;
      }

-结束示例]

5

当且仅当构造了变量时,才会执行具有静态存储持续时间的本地对象的析构函数。[注意: basic.start.term描述了具有静态存储持续时间的本地对象被销毁的顺序。]

回答by Eugene

The memory for all static variables is allocated at program load. But local static variables are created and initialized the first time they are used, not at program start up. There's some good reading about that, and statics in general, here. In general I think some of these issues depend on the implementation, especially if you want to know where in memory this stuff will be located.

所有静态变量的内存在程序加载时分配。但是局部静态变量是在第一次使用时创建和初始化的,而不是在程序启动时。有一些关于这个的很好的阅读,以及一般的静态,这里。一般来说,我认为其中一些问题取决于实现,特别是如果你想知道这些东西在内存中的位置。

回答by Henk

The compiler will allocate static variable(s) defined in a function fooat program load, however the compiler will also add some additional instructions (machine code) to your function fooso that the first time it is invoked this additional code will initialize the static variable (e.g. invoking the constructor, if applicable).

编译器将foo在程序加载时分配在函数中定义的静态变量,但是编译器还会向您的函数添加一些附加指令(机器代码),foo以便第一次调用该附加代码时,此附加代码将初始化静态变量(例如调用构造函数,如果适用)。

@Adam: This behind the scenes injection of code by the compiler is the reason for the result you saw.

@Adam:编译器在幕后注入代码是您看到结果的原因。

回答by Thang Le

I try to test again code from Adam Pierceand added two more cases: static variable in class and POD type. My compiler is g++ 4.8.1, in Windows OS(MinGW-32). Result is static variable in class is treated same with global variable. Its constructor will be called before enter main function.

我尝试再次测试来自Adam Pierce 的代码并添加了另外两个案例:类中的静态变量和 POD 类型。我的编译器是 g++ 4.8.1,在 Windows 操作系统(MinGW-32)中。结果是类中的静态变量与全局变量相同。它的构造函数将在进入 main 函数之前被调用。

  • Conclusion (for g++, Windows environment):

    1. Global variableand static member in class: constructor is called before enter mainfunction (1).
    2. Local static variable: constructor is only called when execution reaches its declaration at first time.
    3. If Local static variable is POD type, then it is also initialized before enter mainfunction (1). Example for POD type: static int number = 10;
  • 结论(适用于 g++、Windows 环境):

    1. 类中的全局变量静态成员:在进入main函数之前调用构造函数(1)
    2. 局部静态变量:构造函数仅在第一次执行到达其声明时调用。
    3. 如果局部静态变量是 POD 类型,那么在进入main函数(1)之前也会对其进行初始化。POD 类型示例:static int number = 10;

(1): The correct state should be: "before any function from the same translation unit is called".However, for simple, as in example below, then it is mainfunction.

(1):正确的状态应该是:“在调用来自同一翻译单元的任何函数之前”。但是,为了简单,如下例所示,它是主要功能。

include < iostream>

包括<iostream>

#include < string>

using namespace std;

class test
{
public:
   test(const char *name)
            : _name(name)
    {
            cout << _name << " created" << endl;
    }

    ~test()
    {
            cout << _name << " destroyed" << endl;
    }

    string _name;
    static test t; // static member
 };
test test::t("static in class");

test t("global variable");

void f()
{
    static  test t("static variable");
    static int num = 10 ; // POD type, init before enter main function

    test t2("Local variable");
    cout << "Function executed" << endl;
}

int main()
{
    test t("local to main");
    cout << "Program start" << endl;
    f();
    cout << "Program end" << endl;
    return 0;
 }

result:

结果:

static in class created
global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed
static in class destroyed

Anybody tested in Linux env ?

有人在 Linux env 中测试过吗?

回答by Rob Walker

Static variables are allocated inside a code segment -- they are part of the executable image, and so are mapped in already initialized.

静态变量在代码段内分配——它们是可执行映像的一部分,因此映射到已经初始化。

Static variables within function scope are treated the same, the scoping is purely a language level construct.

函数作用域内的静态变量被同等对待,作用域纯粹是语言级别的构造。

For this reason you are guaranteed that a static variable will be initialized to 0 (unless you specify something else) rather than an undefined value.

出于这个原因,您可以保证静态变量将被初始化为 0(除非您指定其他内容)而不是未定义的值。

There are some other facets to initialization you can take advantage off -- for example shared segments allow different instances of your executable running at once to access the same static variables.

您可以利用初始化的其他一些方面——例如,共享段允许同时运行的可执行文件的不同实例访问相同的静态变量。

In C++ (globally scoped) static objects have their constructors called as part of the program start up, under the control of the C runtime library. Under Visual C++ at least the order that objects are initialized in can be controlled by the init_segpragma.

在 C++(全局范围)中,静态对象的构造函数在程序启动时被调用,在 C 运行时库的控制下。在 Visual C++ 下,至少对象初始化的顺序可以由init_segpragma控制。

回答by dmityugov

Or is it initialized when doSomething() is first called?

还是在首次调用 doSomething() 时初始化?

Yes, it is. This, among other things, lets you initialize globally-accessed data structures when it is appropriate, for example inside try/catch blocks. E.g. instead of

是的。除其他外,这让您可以在适当的时候初始化全局访问的数据结构,例如在 try/catch 块中。例如代替

int foo = init(); // bad if init() throws something

int main() {
  try {
    ...
  }
  catch(...){
    ...
  }
}

you can write

你可以写

int& foo() {
  static int myfoo = init();
  return myfoo;
}

and use it inside the try/catch block. On the first call, the variable will be initialized. Then, on the first and next calls, its value will be returned (by reference).

并在 try/catch 块中使用它。在第一次调用时,变量将被初始化。然后,在第一次和下一次调用时,它的值将被返回(通过引用)。