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

时间:2008-09-10 23:49:09

标签: c++ variables

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

int globalgarbage;
unsigned int anumber = 42;

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

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

globalish的空间何时分配?我猜测程序什么时候开始。但是它也被初始化了吗?或者首次调用doSomething()时是否已初始化?

7 个答案:

答案 0 :(得分:78)

我很好奇,所以我编写了以下测试程序并用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;
}

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

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

答案 1 :(得分:47)

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

  

3.6.2非本地对象的初始化[basic.start.init]

     

1

     

具有静态存储的对象的存储   duration( basic.stc.static )应为零初始化( dcl.init )   在进行任何其他初始化之前。对象   具有静态存储持续时间的POD类型( basic.types )   用常量表达式( expr.const )初始化的应该是   在进行任何动态初始化之前初始化。   名称空间作用域的对象,其中定义了静态存储持   相同的翻译单元和动态初始化应该是   按其定义出现的顺序初始化   翻译单位。 [注意: dcl.init.aggr 描述了   初始化聚合成员的顺序。该    stmt.dcl 中描述了本地静态对象的初始化。 ]

     

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

     

6.7声明声明[stmt.dcl]

     

...

     

4

     

所有本地对象的零初始化( dcl.init )   静态存储持续时间( basic.stc.static )之前执行   发生任何其他初始化。一个本地对象   具有静态存储持续时间的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 描述了本地的顺序   具有静态存储持续时间的对象被销毁]

答案 2 :(得分:23)

所有静态变量的内存在程序加载时分配。但是本地静态变量在第一次使用时创建并初始化,而不是在程序启动时初始化。对此有一些很好的解读,以及一般的静力学,here。总的来说,我认为其中一些问题取决于实现,特别是如果你想知道这些内容的位置在哪里。

答案 3 :(得分:9)

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

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

答案 4 :(得分:5)

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

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

    1. 全局变量类中的静态成员:在输入函数 (1)之前调用构造函数 即可。
    2. 本地静态变量:构造函数仅在第一次执行到达声明时被调用。
    3. 如果本地静态变量是POD类型,那么在进入功能 (1) 。 POD类型示例: static int number = 10;

(1):正确的状态应为:&#34;在调用来自同一翻译单元的任何函数之前&#34;。但是,为简单起见,如下例所示,它是 main 函数。

包括&lt;的iostream&GT;

#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;
 }

<强>结果:

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环境下测试过吗?

答案 5 :(得分:3)

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

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

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

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

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

答案 6 :(得分:3)

  

或者首次调用doSomething()时是否已初始化?

是的,确实如此。除其他外,这允许您在适当时初始化全局访问的数据结构,例如在try / catch块内部。例如。而不是

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

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

你可以写

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

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