我想知道为什么 C、C++ 和 Java 中的静态变量默认初始化为零?为什么这不适用于局部变量?
7 回答
为什么静态变量是确定性初始化的而局部变量不是?
看看静态变量是如何实现的。它们的内存在链接时分配,它们的初始值也在链接时提供。没有运行时开销。
另一方面,局部变量的内存是在运行时分配的。堆栈必须增长。你不知道以前那里有什么。如果需要,您可以清除该内存(将其归零),但这会产生运行时开销。C++ 哲学是“你不用为你不使用的东西付费”,所以默认情况下它不会将内存归零。
好的,但是为什么静态变量初始化为零,而不是其他值?
好吧,你通常想用那个变量做点什么。但是你怎么知道它是否已经初始化呢?您可以创建一个静态布尔变量。但是它也必须可靠地初始化为某些东西(最好是假的)。指针呢?您宁愿希望它初始化为 NULL 而不是一些随机垃圾。结构/记录怎么样?它里面还有一些其他的数据成员。将它们全部初始化为其默认值是有意义的。但为简单起见,如果您使用“初始化为 0”策略,则不必检查各个成员并检查它们的类型。您可以将整个内存区域初始化为 0。
这并不是真正的技术要求。如果默认值不是 0,初始化的语义仍然可以被认为是合理的,但仍然是确定性的。但是,那个值应该是什么?您可以很容易地解释为什么使用 0(尽管听起来确实有点随意),但解释 -1 或 1024 似乎更难(尤其是变量可能不足以容纳该值等)。
而且您始终可以显式初始化变量。
而且你总是有 C++ 标准的第 8.5.6 段,它说“每个静态存储持续时间的对象都应该在程序启动时进行零初始化”。
有关更多信息,请参阅以下其他问题:
C++ 标准的第 8.5.6 段指出:
“每个静态存储持续时间的对象都应在程序启动时进行零初始化”
(标准还说局部变量的初始化是未定义的)
至于为什么,标准并没有说明 ;) 一种猜测是它相当容易实现,没有任何额外的缺点。
说java:
必须先初始化局部变量,然后才能访问它,因为这是一种安全增益。如果变量已明确设置,编译器会为您检查。
静态或类变量(具有 Object 类型)用 初始化null
,因为编译器无法检查它们是否在编译时初始化。如果程序访问未初始化的变量,它不会让程序失败,而是使用null
.
具有本机类型的变量无法获取null
值,因此非局部变量使用0
or初始化false
,作为后备。当然,这不是最好的解决方案,但我不知道更好的解决方案。;-)
所以在某种程度上,这些只是语言设计者的设计决定。但在 Java 中做出这些决定的可能原因是:
- 对于静态/成员变量,如果您要将它们初始化为某个值,那么零是一个方便的值,因为 (a) 它通常是一个合适的值来表示“未设置为任何其他特殊值”,并且是您将拥有的值在某些情况下(例如柜台)无论如何都会选择;(b) 在内部,很可能零可用于“特殊”值,特别是在对象引用的情况下表示 null。
- 对于局部变量,不给它们默认值允许强制程序员在读取变量之前设置一些值的规则,这实际上对于允许编译器发现某些错误很有用。
在局部变量的情况下,也可以想象一个局部变量可以被声明(在字节码/机器码级别本质上意味着分配堆栈空间/移动堆栈指针)但实际上从未在特定的代码路径中写入/读取。因此,没有默认值可以避免在这些情况下进行不必要的设置默认值工作。
不过,我再说一遍,这些在某种程度上是设计决策。它们本质上是在方便 JVM 实现和方便程序员之间进行权衡。
注意在 C/C++ 中,“静态”变量的含义与 Java 中的静态变量不同!
这只是一个猜测,但它可能是静态的方式,因为它易于实现且有用。
编译器可以将所有变量共同分配到一个连续的内存区域,然后在memset()
调用之前发出代码(一次调用)以清除它main()
。在许多情况下,它还可以依赖操作系统的可执行文件格式的功能,如果该格式支持“ bss 部分”,而这些部分会被加载程序清除。这可以节省可执行文件中的空间,您可以
static unsigned char megabyte[1 << 20];
并且可执行文件不会增长一兆字节。
对于局部变量,这些都不适用;它们是“动态”分配的(通常在堆栈上),清除它们会浪费资源,因为它们通常很快就会被分配。
这与 C/C++ 中“只为使用的东西付费”的概念有关。
对于静态变量,可以在不生成代码的情况下进行初始化。目标文件包含数据段中变量的初始值,当操作系统加载可执行文件时,它会在程序开始执行之前加载并映射该数据段。
对于局部变量,没有代码就无法初始化它们,因为它们不会被初始化一次,每次进入它们的作用域时都应该初始化它们;它们也被分配在堆栈中,并且当分配发生时,通常情况下堆栈中的初始值就是以前的值(除了那些罕见的时刻,您使堆栈增长得比以前增长得更多)。
因此,为了隐式初始化一个局部变量,编译器需要在没有程序员明确命令它这样做的情况下生成代码,这完全违背了那个“哲学”。
关于Java,据我所知,变量总是在程序进入其范围时被初始化,无论它们是否是静态的。它们之间唯一显着的区别是静态变量的作用域是整个程序。鉴于此,他们的行为是一致的。
我对java一无所知,我怀疑java中的statics/locals有什么不同。
至于 c 和 c++,它是关于程序员关心他们的代码效果和喜欢被控制的。初始化局部变量意味着每次程序进入范围时都会执行额外的代码。对于可能是灾难的频繁调用的函数。