【问题标题】:zero initialization and static initialization of local scope static variable局部范围静态变量的零初始化和静态初始化
【发布时间】:2013-07-21 23:38:10
【问题描述】:

我从 Google 阅读了几篇关于 C++ 初始化 的帖子,其中一些将我指向 StackOverflow。我从这些帖子中挑选的概念如下:

  • C++初始化的顺序是:
    1. 零初始化;
    2. 静态初始化;
    3. 动态初始化
  • 静态对象(包括变量)首先是零初始化,然后是静态初始化

我有几个关于初始化问题的询问(存储类问题也可能相关):

  • 全局对象(没有static关键字定义)也是静态对象,对吧?
  • 全局对象也像静态对象一样通过上面的两个步骤进行初始化,对吧?
  • 什么是静态初始化?是否指初始化静态对象(用static关键字定义)?
  • 我还读到,当执行线程第一次进入块时,使用 static 关键字在块内(即在函数中)定义的对象会被初始化!这意味着 本地静态对象ma​​in 函数执行之前不会被初始化。这意味着它们没有像上面提到的两个步骤那样初始化,对吧?
  • 动态初始化是指new操作符创建的对象的初始化,对吧?它可能指的是 myClass obj = myClass(100);myClass obj = foo(); 之类的初始化

我有太多关于初始化和存储类说明符问题的查询。我阅读了 C++2003 标准文档,但找不到清晰的逻辑,因为它们分散在整个文档中。

希望你给我一个答案,逻辑上解释了存储类说明符和初始化的整个映射。欢迎任何参考!

可能解释我的问题的代码:

class myClass{
public:
   int i;
   myClass(int j = 10): j(i){}
   // other declarations
};

myClass obj1;//global scope
static myClass obj2(2);//file scope
{   //local scope
   myClass obj3(3);
   static myClass obj4(4);
}

编辑
如果你觉得我的问题比较繁琐,可以根据上面的代码帮忙解释一下。

【问题讨论】:

标签: c++ static initialization global-variables


【解决方案1】:

我从 Google 阅读了几篇关于 C++ 初始化 的帖子,其中一些将我指向 StackOverflow。我从这些帖子中挑选的概念如下:

  • C++初始化的顺序是:
    1. 零初始化;
    2. 静态初始化;
    3. 动态初始化

是的,确实有 3 个阶段(在标准中)。让我们在继续之前澄清它们:

  • 零初始化:内存在字节级别用0填充。
  • 常量初始化:在对象的内存位置复制预先计算的(编译时)字节模式
  • 静态初始化:零初始化后常量初始化
  • 动态初始化:执行一个函数来初始化内存

一个简单的例子:

int const i = 5;     // constant initialization
int const j = foo(); // dynamic initialization
  • 静态对象(包括变量)首先是零初始化,然后是静态初始化

是和不是。

标准要求对象首先进行零初始化,然后:

  • 尽可能初始化常量
  • 否则动态初始化(编译器无法在编译时计算内存内容)

注意:在常量初始化的情况下,编译器可能会忽略遵循 as-if 规则的第一个零初始化内存。

我有几个关于初始化问题的询问(存储类问题也可能相关):

  • 全局对象(没有static关键字定义)也是静态对象,对吧?

是的,在文件范围内,static 对象只是符号的可见性。可以通过名称从另一个源文件中引用全局对象,而 static 对象名称完全是当前源文件的本地名称。

混乱源于static在许多不同情况下的重用:(

  • 全局对象也像静态对象一样通过上面的两个步骤进行初始化,对吧?

是的,实际上是本地静态对象。

  • 什么是静态初始化?是否指初始化静态对象(用static关键字定义)?

不,如上所述,它指的是在不执行用户定义函数的情况下初始化对象,而是将预先计算的字节模式复制到对象的内存中。请注意,对于稍后将动态初始化的对象,这只是将内存归零。

  • 我还读到,当执行线程第一次进入块时,使用 static 关键字在块内(即在函数中)定义的对象会被初始化!这意味着 本地静态对象ma​​in 函数执行之前不会被初始化。这意味着它们没有像上面提到的两个步骤那样初始化,对吧?

它们通过两个步骤过程进行初始化,尽管实际上只有第一次执行通过它们的定义。所以过程是一样的,只是时间略有不同。

但在实践中,如果它们的初始化是静态的(即内存模式是编译时模式)并且它们的地址没有被取走,它们可能会被优化掉。

请注意,在动态初始化的情况下,如果它们的初始化失败(应该初始化它们的函数抛出异常),它将在下一次流控制通过它们的定义时重新尝试。

  • 动态初始化是指new操作符创建的对象的初始化,对吧?它可能指的是 myClass obj = myClass(100);myClass obj = foo(); 之类的初始化

根本不是,它是指需要执行用户定义函数的初始化(注意:std::string 就 C++ 语言而言具有用户定义的构造函数)。

编辑:感谢 Zach 指出我错误地将 C++11 标准称为常量初始化的静态初始化;现在应该修复此错误。

【讨论】:

  • Matthieu,你的解释非常有用。全局或静态对象(本地或非本地)首先在编译时(链接阶段)零初始化,然后在编译时静态初始化动态初始化 在加载时(就在 main 函数之前)。
  • 在您的示例中,它们是两个整数。那么如果是两个对象呢。例如,在我的代码中,myClass obj1; myClass obj2 = myClass(10); myClass obj3(20); myClass obj4 = foo();。这些对象都需要在初始化第二步进行动态初始化还是静态初始化?
  • @Zack:他们通常需要动态初始化,但是如果编译器可以证明您不会注意到更改,则允许编译器根据 as-if 规则将其替换为静态初始化。
  • Matthieu,你能验证我的第一条评论吗?真假。你提到的as-if rule指的是什么?顺便说一句,如果第二步是静态初始化,那么零和静态初始化什么时候发生?由于它们都是在字节级别计算的编译时间,首先设置为 0,然后设置为其他值(如 5)的基本原理是什么
  • @Zack:“假设”规则是,只要可观察到的行为“假设”它们没有发生,就允许优化。零初始化和然后静态初始化的想法是,一些操作系统会先将它们分配的内存清零,然后再将其交给程序,然后可以memcpy在其上使用图像,但实际上这是不可观察的(因为从程序中您只能在静态初始化结束后访问内存)。
【解决方案2】:

我相信有三个不同的概念:初始化变量、变量在内存中的位置、变量初始化的时间。

首先:初始化

当一个变量被分配到内存中时,典型的处理器会保持内存不变,因此该变量将具有与其他人之前存储的相同的值。为了安全起见,一些编译器添加了额外的代码来将它们分配的所有变量初始化为零。我认为这就是您所说的“零初始化”。当你说:

 int i; // not all compilers set this to zero

但是,如果你对编译器说:

 int i = 10;

然后编译器指示处理器将 10 放入内存中,而不是将其保留为旧值或将其设置为零。我认为这就是您所说的“静态初始化”。

最后,你可以这样说:

 int i;
 ...
 ...
 i = 11;

然后处理器在执行int i; 时“零初始化”(或保留旧值),然后当它到达i = 11 行时,它“动态初始化”变量为11(这可能在第一次初始化后很长时间发生.

二:变量的位置

有:基于堆栈的变量(有时称为静态变量)和内存堆变量(有时称为动态变量)。

可以使用以下方法在堆栈段中创建变量:

int i;

或者像这样的内存堆:

int *i = new int;

不同之处在于退出函数调用后栈段变量丢失,而内存堆变量则一直保留到你说delete i;。您可以阅读汇编语言书籍以更好地了解其中的区别。

三:变量初始化的时间

当您输入定义它们的函数调用时,堆栈段变量是“零初始化”或静态初始化的。

new 运算符首次创建内存堆变量时,它是“零初始化”或静态初始化的。

结语

您可以将static int i; 视为一个全局变量,其范围仅限于定义它的函数。我认为对static int i; 的混淆是因为静态听到意味着另一件事(退出时它不会被破坏)例程,因此它保留其价值)。我不确定,但我认为 static int i; 使用的技巧是将其放入 main() 的堆栈中,这意味着它在退出整个程序之前不会被销毁(因此它保留第一次初始化),或者它可以是存储在应用程序的数据段中。

【讨论】:

  • 将我的回答与 Matthieu M. 的回答进行比较,我看不出有多大区别!奇怪!
  • 好吧,至少三个人不同意。具体来说 - 您对“静态初始化”的定义是错误的,您将 static int i 描述为“基于堆栈的变量”是错误的,并且您需要阅读有关汇编语言以了解更多关于动态分配的声明是错误的。除此之外,new 的代码示例是错误的。还有更多,但是当已经有另一个完全正确的答案时,几乎不值得再把它分开。
  • “当一个变量被分配到内存中时,典型的处理器会保持内存不变,因此该变量将具有与其他人之前存储的相同的值。” - “so”后面的部分是错误的。能够读取别人的内存是多用户操作系统所不允许的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 2010-12-22
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多