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

int globalgarbage;
unsigned int anumber = 42;

但是函数内定义的静态变量又如何呢?

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

什么时候有空间 globalish 分配?我猜程序什么时候开始。但它也会被初始化吗?或者是在什么时候初始化的 doSomething() 首先被调用?

有帮助吗?

解决方案

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

其他提示

C++ 标准中的一些相关用语:

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

1

具有静态存储持续时间的对象的存储(基本.stc.静态)应为零初始化(初始化程序)在发生任何其他初始化之前。POD类型的对象(基本类型)具有静态存储持续时间初始化,并具有恒定表达式(表达式常量)应在发生任何动态初始化之前初始化。具有同一翻译单元和动态初始化的静态存储持续时间的命名空间范围的对象应按照其定义出现在翻译单元中的顺序进行初始化。[笔记: dcl.init.aggr 描述汇总成员初始化的顺序。局部静态对象的初始化描述 stmt.dcl. ]

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

6.7 声明语句[stmt.dcl]

...

4

零初始化(初始化程序)所有静态存储持续时间的本地对象(基本.stc.静态)在发生任何其他初始化之前执行。POD类型的本地对象(基本类型)在首先输入其块之前初始化了具有恒定表达的静态存储持续时间。允许实现在与实现的情况下静态初始化具有静态存储持续时间的其他本地对象的早期初始化,以允许实现在命名空间范围中静态初始化具有静态存储持续时间的对象(基本启动.init)。否则,首次控制通过其声明初始化了这样的对象。这样的对象在其初始化完成后被视为初始化。如果初始化通过抛出异常而退出,则初始化尚未完成,因此下次控制进入声明时,将再次对其进行尝试。如果控制对象被初始化时重新输入声明(递归),则行为是不确定的。[例子:

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

--结束示例]

5

且仅当构造变量时,才能执行具有静态存储持续时间的本地对象的驱动器。[笔记: 基本.开始.术语 描述具有静态存储持续时间的本地对象的顺序。]

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

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

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

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

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

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

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

包含<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;
 }

结果:

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

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

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

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

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

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

或者它是在第一次调用 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 块内使用它。第一次调用时,变量将被初始化。然后,在第一次和下一次调用时,将返回其值(通过引用)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top