我非常有信心在程序启动时分配(并初始化,如果适用)全局声明的变量。
int globalgarbage;
unsigned int anumber = 42;
但是函数中定义的静态变量呢?
void doSomething()
{
static bool globalish = true;
// ...
}
什么时候globalish
分配空间?我猜该程序何时启动。但是它也会被初始化吗?还是在doSomething()
第一次调用时初始化?
Answers:
我对此感到很好奇,因此我编写了以下测试程序,并使用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个
具有静态存储持续时间(basic.stc.static)的对象的存储应在进行任何其他初始化之前进行零初始化(dcl.init)。具有静态存储持续时间的POD类型(basic.types)的对象(使用常量表达式(expr.const)初始化)应在进行任何动态初始化之前进行初始化。在同一翻译单元中定义并动态初始化且具有静态存储持续时间的名称空间范围的对象,应按照其定义在翻译单元中出现的顺序进行初始化。[注意: 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 描述具有静态存储持续时间的本地对象的销毁顺序。]
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.
所有静态变量的内存在程序加载时分配。但是局部静态变量是在第一次使用它们时创建的,并在程序启动时进行了初始化。还有关于一些良好的阅读,和静在一般情况下,在这里。总的来说,我认为其中一些问题取决于实现方式,尤其是如果您想知道这些东西在内存中的位置。
我尝试再次测试来自Adam Pierce的代码,并添加了另外两种情况:类中的静态变量和POD类型。我的编译器是Windows OS(MinGW-32)中的g ++ 4.8.1。结果是类中的静态变量与全局变量相同。在进入主函数之前将调用其构造函数。
结论(对于g ++,Windows环境):
(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 ++下,至少可以通过init_seg编译指示来控制对象的初始化顺序。
还是在首次调用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块中使用它。在第一次调用时,变量将被初始化。然后,在第一次和下一次调用时,将返回其值(通过引用)。