静态变量初始化?

时间:2009-12-02 07:53:57

标签: java c++ c static-initialization

我想知道为什么C,C ++和Java中的静态变量默认初始化为零?为什么局部变量不适用呢?

7 个答案:

答案 0 :(得分:20)

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

了解静态变量的实现方式。 它们的内存在链接时分配,它们的初始值也在链接时提供。没有运行时开销。

另一方面,局部变量的内存在运行时分配。堆栈必须增长。你不知道之前有什么。如果需要,可以清除该内存(零),但这会导致运行时开销。 C ++理念是“你不为你不使用的东西买单”,所以它默认不会将内存归零。

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

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

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

您始终可以明确初始化变量。

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

有关详情,请参阅以下其他问题:

答案 1 :(得分:4)

C ++标准的第8.5.6段规定:

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

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

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

答案 2 :(得分:3)

说到java:

必须先初始化局部变量才能访问它,因为这是一个安全的好处。如果变量是确定的,编译器会检查你。

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

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

答案 3 :(得分:2)

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

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

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

但是,我再说一遍,这些都是设计决定。它们本质上是在JVM实现方便和程序员方便之间的权衡。

N.B。在C / C ++中,“静态”变量对Java中的静态变量意味着不同的东西!

答案 4 :(得分:1)

我不知道java,我怀疑它与java中的静态/本地不同。

至于c和c ++,它是关于程序员关心他们的代码效果和喜欢控制。每次程序进入范围时,初始化局部变量都意味着执行额外的代码。对于经常被称为可能是灾难的功能。

答案 5 :(得分:1)

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

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

对于局部变量,没有代码就无法初始化它们,因为它们没有初始化一次,每次进入其范围时都应初始化它们;它们也被分配在堆栈中,并且当分配发生时,一般情况下堆栈中的初始值就是之前的情况(除了那些难得的时刻,你的堆栈增长比之前增长的更多)。

因此,为了隐式初始化一个局部变量,编译器需要生成代码而程序员没有明确地命令它这样做,这完全违背了“哲学”。

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

答案 6 :(得分:0)

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

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

static unsigned char megabyte[1 << 20];

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

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