关于c ++:什么时候分配/初始化函数级静态变量?

关于c ++:什么时候分配/初始化函数级静态变量?

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

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

1
2
int globalgarbage;
unsigned int anumber = 42;

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

1
2
3
4
5
void doSomething()
{
  static bool globalish = true;
  // ...
}

globalish的空间何时分配? 我猜该程序何时启动。 但是它也会被初始化吗? 还是在第一次调用doSomething()时初始化?


我对此感到很好奇,因此我编写了以下测试程序,并使用g ++ 4.1.2版对其进行了编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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;
}

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

1
2
3
4
5
6
7
8
9
10
11
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

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.aggr describes 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:

1
2
3
4
5
      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.term describes the order in which local
objects with static storage duration are destroyed. ]


所有静态变量的内存在程序加载时分配。但是局部静态变量是在第一次使用它们时创建的,而不是在程序启动时进行初始化。关于这一点,这里有一些不错的读物,以及一般的静态知识。总的来说,我认为其中一些问题取决于实现方式,尤其是如果您想知道这些东西在内存中的位置。


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

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


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

  • 结论(对于g ++,Windows环境):

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

(1):正确的状态应为:"在调用同一翻译单元中的任何函数之前"。但是,为简单起见,如以下示例所示,它是主要功能。

包括

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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;
 }

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

有人在Linux环境中测试过吗?


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

是的。除此之外,它还使您可以在合适的时候初始化全局访问的数据结构,例如在try / catch块内部。例如。代替

1
2
3
4
5
6
7
8
9
10
int foo = init(); // bad if init() throws something

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

你可以写

1
2
3
4
int& foo() {
  static int myfoo = init();
  return myfoo;
}

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


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

函数范围内的静态变量被视为相同,作用域纯粹是语言级别的构造。

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

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

在C ++(全局范围内)中,静态对象的构造函数在C运行时库的控制下作为程序启动的一部分被调用。在Visual C ++下,至少可以通过init_seg编译指示来控制对象的初始化顺序。


推荐阅读

    excel怎么用乘法函数

    excel怎么用乘法函数,乘法,函数,哪个,excel乘法函数怎么用?1、首先用鼠标选中要计算的单元格。2、然后选中单元格后点击左上方工具栏的fx公

    excel中乘法函数是什么?

    excel中乘法函数是什么?,乘法,函数,什么,打开表格,在C1单元格中输入“=A1*B1”乘法公式。以此类推到多个单元。1、A1*B1=C1的Excel乘法公式

    标准差excel用什么函数?

    标准差excel用什么函数?,函数,标准,什么,在数据单元格的下方输入l标准差公式函数公式“=STDEVPA(C2:C6)”。按下回车,求出标准公差值。详细

    excel常用函数都有哪些?

    excel常用函数都有哪些?,函数,哪些,常用,1、SUM函数:SUM函数的作用是求和。函数公式为=sum()例如:统计一个单元格区域:=sum(A1:A10)  统计多个

    格式化分配单元的大小是什么意思

    格式化分配单元的大小是什么意思,,格式化分配单元的大小是什么意思这是今天的光;问一个百事网小编的问题。对于这个问题,事实上,互联网用户要

    电脑怎么分配cpu|电脑怎么分配c盘

    电脑怎么分配cpu|电脑怎么分配c盘,,1. 电脑怎么分配c盘最快最安全的方法是:转移重要数据,之后重分区,重装系统你想不重装系统来调整、合并