【问题标题】:Static variable initialization?静态变量初始化?
【发布时间】:2010-12-22 07:46:27
【问题描述】:

我想知道为什么 C、C++ 和 Java 中的静态变量默认初始化为零?为什么局部变量不是这样?

【问题讨论】:

  • 你说的是哪种语言?
  • 它不是特定于语言的,而是关于概念的?在所有语言 C、C++、Java 中,静态变量的默认初始化为零
  • 不,Ton,静态变量是默认初始化的。

标签: java c++ c static-initialization


【解决方案1】:

C++ 标准的第 8.5.6 段指出:

"每个静态存储持续时间的对象在程序启动时都应进行零初始化"

(标准也说局部变量的初始化是未定义的)

至于为什么,标准没有说明 ;) 一种猜测是它相当容易实现,没有任何额外的缺点。

【讨论】:

  • 他可能想知道为什么会做出这个选择。
  • 是的,这是真的,但我的问题是为什么?
  • 而另一方面是本地的默认初始化会导致性能下降。而且 C++ 设计者更关注性能,而不是编写正确程序的难易程度。
  • Stephen C,与其说是性能,不如说是对性能的控制。不仅是性能。从长远来看,这实际上简化了编写正确的程序;-)
【解决方案2】:

为 java 说话:

必须先初始化局部变量,然后才能访问它,因为这是一种安全增益。如果变量已明确设置,编译器会为您检查。

静态或类变量(具有 Object 类型)使用 null 初始化,因为编译器无法检查它们是否在编译时初始化。如果程序访问未初始化的变量,它不会让程序失败,而是使用null 隐式初始化。

具有本机类型的变量无法获得 null 值,因此作为后备,使用 0false 初始化非局部变量。当然,这不是最好的解决方案,但我不知道更好的解决方案。 ;-)

【讨论】:

  • 其实从JVM的角度来看,局部变量在初始化之前根本就不存在。
  • 编译器检查最终字段是否已分配,并且在分配之前没有明显使用。但是,仍有一些方法可以在分配之前查看字段(本地人并非如此)。非最终静态字段应该非常罕见(应该)。它保留了为什么不认为使用默认值保留字段可能是错误的原因。
  • @Tom - 您的“开放”问题的答案是,为此提出 JLS 规则将非常困难 1)不需要分析程序中的所有类,并且2)应对动态加载。
【解决方案3】:

这只是一个猜测,但它可能是静态的方式,因为它易于实现且有用。

编译器可以将所有变量共同分配到一个连续的内存区域,然后在调用main() 之前发出代码(单个memset() 调用)以清除它。在许多情况下,它还可以依赖操作系统的可执行文件格式的功能,如果该格式支持“bss sections"”,则由加载程序清除。这样可以节省可执行文件中的空间,您可以拥有

static unsigned char megabyte[1 << 20];

并且可执行文件不会增长一兆字节。

对于局部变量,这些都不适用;它们是“动态”分配的(通常在堆栈上),清除它们会浪费资源,因为它们通常很快就会被分配。

【讨论】:

    【解决方案4】:

    我对java一无所知,我怀疑java中的statics/locals有什么不同。

    对于 c 和 c++,它是关于程序员关心他们的代码效果和热爱控制的。初始化局部变量意味着每次程序进入范围时都会执行额外的代码。对于可能会造成灾难的频繁调用的函数。

    【讨论】:

    • 写得很好,但这不是我问题的答案
    • 不确定你想要什么。如果你拒绝这个,我希望你接受安德烈亚斯的回答。他引用了标准,我给你背后的原因。你在寻找什么答案?
    • 这假设编译器真的很笨。在几乎所有情况下,很容易在使用前查看变量是否已分配。
    • 这与你指控我的假设有什么关系?
    • @hacker,我想知道为什么静态变量被初始化为零?
    【解决方案5】:

    所以在某种程度上,这些只是语言设计者的设计决定。但在 Java 中做出这些决定的可能原因是:

    • 对于静态/成员变量,如果您要将它们初始化为某个值,那么零是一个方便的值,因为 (a) 它通常是一个合适的值来表示“未设置为任何其他特殊值”,并且是值在某些情况下,例如柜台,您无论如何都会选择; (b) 在内部,零很可能用于“特殊”值,特别是在对象引用的情况下表示 null。
    • 对于局部变量,不给它们默认值允许强制程序员在读取变量之前设置一些值的规则,这实际上有助于编译器发现某些错误。

    在局部变量的情况下,还可以想象,可以声明一个局部变量(在字节码/机器码级别,这本质上意味着分配堆栈空间/移动堆栈指针),但随后从未实际写入/读取特定的代码路径。因此,没有默认值可以避免在这些情况下进行不必要的设置默认值工作。

    不过,我再说一遍,这些在某种程度上是设计决策。它们本质上是在方便 JVM 实现和方便程序员之间进行权衡。

    注意在 C/C++ 中,“静态”变量的含义与 Java 中的静态变量不同!

    【讨论】:

      【解决方案6】:

      为什么静态变量是确定性初始化而局部变量不是?

      看看静态变量是如何实现的。 它们的内存在链接时分配,它们的初始值也在链接时提供。没有运行时开销。

      另一方面,局部变量的内存是在运行时分配的。堆栈必须增长。你不知道以前那里有什么。如果需要,您可以清除该内存(将其归零),但这会产生运行时开销。 C++ 理念是“不用为不用的东西付费”,因此默认情况下它不会将内存归零。

      好的,但是为什么静态变量初始化为零,而不是其他值?

      嗯,你通常想用那个变量做点什么。但是你怎么知道它是否已经初始化呢?您可以创建一个静态布尔变量。但是它也必须可靠地初始化为某些东西(最好是假的)。 指针怎么样?您宁愿希望它初始化为 NULL 而不是一些随机垃圾。 结构/记录怎么样?它里面还有一些其他的数据成员。将它们全部初始化为其默认值是有意义的。但为简单起见,如果您使用“初始化为 0”策略,则不必检查各个成员并检查它们的类型。 您可以将整个内存区域初始化为 0。

      这并不是真正的技术要求。如果默认值不是 0,那么初始化的语义仍然可以被认为是合理的,但仍然是确定性的。但是,那个值应该是什么?您可以很容易地解释为什么使用 0(尽管听起来确实有些随意),但解释 -1 或 1024 似乎更难(尤其是变量可能不足以容纳该值等)。

      而且你总是可以显式初始化变量。

      C++ 标准的第 8.5.6 段总是说“每个静态存储持续时间的对象都应在程序启动时进行零初始化”。

      有关更多信息,请参阅以下其他问题:

      【讨论】:

      • 是的,我同意静态变量的内存是在链接时分配的,但为什么默认为零。
      • 我扩展了我的答案,希望它包含您感兴趣的信息。
      • 那么类对象呢,确定它的构造函数不会在链接时被调用,那么如果它在链接时被初始化,它只是被初始化为一个POD,这似乎没有意义?
      • 它们的内存 [静态变量] 在链接时分配 [这可能取决于平台] -- 并非如此,而是在加载时,当二进制文件加载到内存中时。特别是,对于非零值,二进制文件必须包含值[使二进制文件变大],但对于零值,它根本不需要保存变量,它只需要通知加载程序保留空间[为所有静态归零 vars 就足够了]。这也与 zero 值有关,它根本不是任意的。此外,许多操作系统已经为您清零了内存。
      【解决方案7】:

      这与 C/C++ 中“只为使用的东西付费”的概念有关。

      对于静态变量,可以在不生成代码的情况下进行初始化。目标文件包含数据段中变量的初始值,当操作系统加载可执行文件时,它会在程序开始执行之前加载并映射此数据段。

      对于局部变量,没有代码就无法初始化它们,因为它们不会被初始化一次,每次进入它们的作用域时都应该初始化它们;它们也被分配在堆栈中,并且当分配发生时,通常情况下堆栈中的初始值就是之前的值(除了那些罕见的时刻,您使堆栈增长得比以前增长得更多)。

      所以要隐式初始化一个局部变量,编译器需要在没有程序员明确命令的情况下生成代码,这完全违背了“哲学”。

      关于Java,据我所知,变量总是在程序进入其作用域时被初始化,无论它们是否是静态的。它们之间唯一显着的区别是静态变量的作用域是整个程序。鉴于此,他们的行为是一致的。

      【讨论】:

        猜你喜欢
        • 2011-08-22
        • 2010-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多