C++ 编译器是如何实现静态变量初始化的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/898432/
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
How is static variable initialization implemented by the compiler?
提问by e.James
I'm curious about the underlying implementation of static variables within a function.
我很好奇函数中静态变量的底层实现。
If I declare a static variable of a fundamental type (char, int, double, etc.), and give it an initial value, I imagine that the compiler simply sets the value of that variable at the very beginning of the program before main()
is called:
如果我声明一个基本类型(char、int、double 等)的静态变量,并给它一个初始值,我想编译器只是在main()
调用之前在程序的最开始设置该变量的值:
void SomeFunction();
int main(int argCount, char ** argList)
{
// at this point, the memory reserved for 'answer'
// already contains the value of 42
SomeFunction();
}
void SomeFunction()
{
static int answer = 42;
}
However, if the static variable is an instance of a class:
但是,如果静态变量是类的实例:
class MyClass
{
//...
};
void SomeFunction();
int main(int argCount, char ** argList)
{
SomeFunction();
}
void SomeFunction()
{
static MyClass myVar;
}
I know that it will not be initialized until the first time that the function is called. Since the compiler has no way of knowing when the function will be called for the first time, how does it produce this behavior? Does it essentially introduce an if-block into the function body?
我知道在第一次调用该函数之前它不会被初始化。由于编译器无法知道该函数何时会被第一次调用,它是如何产生这种行为的呢?它本质上是否在函数体中引入了一个 if 块?
static bool initialized = 0;
if (!initialized)
{
// construct myVar
initialized = 1;
}
采纳答案by Michael Burr
In the compiler output I have seen, function local static variables are initialized exactly as you imagine.
在我看到的编译器输出中,函数局部静态变量完全按照您的想象进行初始化。
Note that in general this is notdone in a thread-safe manner. So if you have functions with static locals like that that might be called from multiple threads, you should take this into account. Calling the function once in the main thread before any others are called will usually do the trick.
请注意,通常这不是以线程安全的方式完成的。因此,如果您有可能从多个线程调用的具有静态局部变量的函数,您应该考虑到这一点。在调用任何其他函数之前在主线程中调用一次该函数通常会奏效。
I should add that if the initialization of the local static is by a simple constant like in your example, the compiler doesn't need to go through these gyrations - it can just initialize the variable in the image or before main()
like a regular static initialization (because your program wouldn't be able to tell the difference). But if you initialize it with a function's return value, then the compiler pretty much has to test a flag indicating if the initialization has been done or something equivalent.
我应该补充一点,如果本地静态的初始化是通过像您的示例中那样的简单常量,则编译器不需要经过这些旋转 - 它可以main()
像常规静态初始化一样初始化图像中或之前的变量(因为您的程序将无法区分差异)。但是如果你用函数的返回值初始化它,那么编译器几乎必须测试一个标志,指示初始化是否已经完成或等效的东西。
回答by James Hopkin
This questioncovered similar ground, but thread safety wasn't mentioned. For what it's worth, C++0x will make function static initialisation thread safe.
这个问题涵盖了类似的基础,但没有提到线程安全。就其价值而言,C++0x 将使函数静态初始化线程安全。
(see the C++0x FCD, 6.7/4 on function statics: "If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.")
(参见C++0x FCD6.7/4 关于函数静态:“如果在初始化变量时控制同时进入声明,则并发执行应等待初始化完成。”)
One other thing that hasn't been mentioned is that function statics are destructed in reverse order of their construction, so the compiler maintains a list of destructors to call on shutdown (this may or may not be the same list that atexit uses).
另一件没有提到的事情是函数静态是按照它们构造的相反顺序被破坏的,所以编译器维护了一个在关闭时调用的析构函数列表(这可能是也可能不是 atexit 使用的相同列表)。
回答by Steve Jessop
You're right about everything, including the initialized flag as a common implementation. This is basically why initialization of static locals is not thread-safe, and why pthread_once exists.
你对一切都是正确的,包括初始化标志作为一个常见的实现。这基本上就是为什么静态局部变量的初始化不是线程安全的,以及 pthread_once 存在的原因。
One slight caveat: the compiler must emit code which "behaves as if" the static local variable is constructed the first time it is used. Since integer initialization has no side effects (and calls no user code), it's up to the compiler when it initializes the int. User code cannot "legitimately" find out what it does.
一个小小的警告:编译器必须发出代码,该代码“表现得好像”静态局部变量在第一次使用时被构造。由于整数初始化没有副作用(并且不调用用户代码),因此在初始化 int 时取决于编译器。用户代码不能“合法地”找出它的作用。
Obviously you can look at the assembly code, or provoke undefined behaviour and make deductions from what actually happens. But the C++ standard doesn't count that as valid grounds to claim that the behaviour is not "as if" it did what the spec says.
显然,您可以查看汇编代码,或者引发未定义的行为并根据实际发生的情况进行推断。但是 C++ 标准并不认为这是有效的理由,声称该行为不是“好像”它做了规范所说的那样。
回答by ChrisW
I know that it will not be initialized until the first time that the function is called. Since the compiler has no way of knowing when the function will be called for the first time, how does it produce this behavior? Does it essentially introduce an if-block into the function body?
我知道在第一次调用该函数之前它不会被初始化。由于编译器无法知道该函数何时会被第一次调用,它是如何产生这种行为的呢?它本质上是否在函数体中引入了一个 if 块?
Yes, that's right: and, FWIW, it's not necessarily thread-safe (if the function is called "for the first time" by two threads simultaneously).
是的,没错:而且,FWIW,它不一定是线程安全的(如果两个线程同时“第一次”调用该函数)。
For that reason you might prefer to define the variable at global scope (although maybe in a class or namespace, or static without external linkage) instead of inside a function, so that it's initialized before the program starts without any run-time "if".
出于这个原因,您可能更喜欢在全局范围内定义变量(尽管可能在类或命名空间中,或者在没有外部链接的情况下是静态的)而不是在函数内部,以便在程序启动之前对其进行初始化而没有任何运行时“if” .
回答by jesup
Another twist is in embedded code, where the run-before-main() code (cinit/whatever) may copy pre-initialized data (both statics and non-statics) into ram from a const data segment, perhaps residing in ROM. This is useful where the code may not be running from some sort of backing store (disk) where it can be re-loaded from. Again, this doesn't violate the requirements of the language, since this is done before main().
另一个扭曲是在嵌入式代码中,其中 run-before-main() 代码(cinit/任何)可以将预初始化的数据(静态和非静态)从 const 数据段复制到 ram 中,可能驻留在 ROM 中。这在代码可能不是从某种可以重新加载的后备存储(磁盘)运行的情况下很有用。同样,这并不违反语言的要求,因为这是在 main() 之前完成的。
Slight tangent: While I've not seen it done much (outside of Emacs), a program or compiler could basically run your code in a process and instantiate/initialize objects, then freeze and dump the process. Emacs does something similar to this to load up large amounts of elisp (i.e. chew on it), then dump the running state as the working executable, to avoid the cost of parsing on each invocation.
稍微切线:虽然我没有看到它做了太多(在 Emacs 之外),但程序或编译器基本上可以在进程中运行您的代码并实例化/初始化对象,然后冻结和转储进程。Emacs 做了一些类似的事情来加载大量的 elisp(即咀嚼它),然后将运行状态转储为工作可执行文件,以避免每次调用解析的成本。