初始化变量有多重要


9

初始化变量有多重要?

适当的初始化是否可以避免内存泄漏或具有性能优势?


14
这取决于语言。在某些语言中,防止错误非常重要,而在其他语言中,提高可读性只是一件好事。
Telastyn 2014年

感谢Telastyn的输入。您能否根据语言来说明它变得很重要?
Vivek 2014年

4
C ++在这里是臭名昭著的。在调试中,局部变量null由通用编译器初始化为0(或),但在进行发行时则为随机垃圾。(尽管我的C ++知识来自大约10年前,但情况可能已经发生了变化)
Telastyn 2014年

这是一次烧两次害羞的情况。由于我已经看到/有由未初始化的变量(尤其是指针)引起的错误,因此这已成为一种习惯。对于性能而言,通常是无关紧要的。对于内存泄漏,这并不是一个真正的问题。
Mike Dunlavey 2014年

1
@Telastyn比那更糟。未定义的行为不仅限于垃圾状态,任何事情都可能发生。编译器可以假定读取未初始化变量的路径是不可达的,并消除了在此过程中发生的“无关”影响。
卡雷斯(Caleth)'18

Answers:


7

未初始化的变量使程序不确定。每次程序运行时,它的行为都可能不同。操作环境,一天中的时间,月相以及它们的排列等无关的更改会影响这些守护程序的显示方式和时间。该程序可能在缺陷出现之前运行一百万次,他们可能每次都运行一次,或者运行另一百万次。许多问题归结为“故障”而被忽略,或者来自客户的缺陷报告被关闭为“不可重现”。您多久重启一次计算机以“解决”问题?您多久对客户说一次“从未见过这种情况,请告诉我您是否再见到它”-希望他们(他们)完全知道他们不会!

由于缺陷的重现在测试环境中几乎是不可能的,因此几乎不可能找到并修复。

错误可能要花费数年才能浮出水面,通常在代码中被认为是可靠和稳定的。缺陷被认为是在最近的代码中-追踪到它可能花费更长的时间。更改编译器,更改编译器,甚至添加一行代码也可以更改行为。

初始化变量具有巨大的性能优势,不仅因为正确运行的程序要比不正常运行的程序快得多,而且开发人员花费更少的时间查找和修复不应存在的缺陷,而将更多的时间用于“实际”工作。

初始化变量的另一个重要优点是代码的原始作者必须决定将变量初始化为什么。这并不总是琐碎的练习,当不是琐碎的练习时,可能表明设计不佳。

内存泄漏是一个不同的问题,但是正确的初始化不仅可以帮助防止它们,还可以帮助检测它们并找到源-高度依赖于语言,这确实是一个单独的问题,值得我进一步探讨。在这个答案。

编辑:在某些语言(例如C#)中,无法使用未初始化的变量,因为程序将无法编译,或者在执行时报告错误(如果完成)。但是,许多具有这些特征的语言都具有与潜在不安全代码的接口,因此在使用此类接口时不要引入未初始化的变量,请务必小心。


6
许多编程语言会自动将其变量设置为某个预定义值,因此您在此处所说的大部分内容不适用于这些语言。
罗伯特·哈维

2
只是重申@RobertHarvey所说的话,这些都不适用于C#。在声明变量时初始化变量没有性能优势,并且无法使用未初始化的变量,因此不能将无法复制的错误归咎于此。(这可能使用未初始化的类字段,但它得到设置为默认值,在这种情况下产生的警告)
BOBSON

4
@mattnz-关键是对于行为类似于C#(或Java)的语言,其中一些建议具有误导性或完全错误。作为一种语言无关的问题,它应该有一个语言无关的反应,这意味着要解决哪些语言做的手柄初始化的变量安全以及那些不这样做。
Bobson,2014年

1
我还想补充一点,初始化的变量问题都不难找到任何像样的编译器/静态分析仪将发出警告他们
JK。

1
对于Java(可能是C#)来说,过早初始化本地是不必要的,并且可能导致更多错误。例如,在有条件分配变量之前将其设置为null会破坏编译器告诉您代码中的路径之一可能不会导致分配变量的能力。
JimmyJames

7

如Telastyn指出的那样,初始化变量可以防止错误。如果变量是引用类型,则对其进行初始化可以防止空引用错误。

具有非空默认值的任何类型的变量都将占用一些内存来存储默认值。


6

尝试使用未初始化的变量始终是一个错误,因此将该错误发生的可能性降到最低是有意义的。

编程语言缓解该问题的最常用方法可能是自动初始化为默认值,因此至少如果您忘记初始化变量,它将类似于0而不是0x16615c4b

如果您碰巧需要将变量初始化为零,则可以解决大部分错误。但是,使用一个初始化为错误值的变量与使用一个根本没有初始化的变量一样糟糕。实际上,有时甚至更糟,因为错误可能更微妙且难以检测。

函数式编程语言不仅通过禁止未初始化的值,而且通过完全禁止重新分配来解决此问题。这就消除了问题,并没有像您想象的那样严格。即使在非功能性语言中,如果您等待声明一个变量,直到拥有一个正确的值来对其进行初始化,那么您的代码往往会更健壮。

就性能而言,它可以忽略不计。最糟糕的情况是,对于未初始化的变量,您会额外分配一个空间,并且占用一些内存的时间超过了必要。好的编译器可以在很多情况下优化差异。

内存泄漏是完全不相关的,尽管正确初始化的变量往往会在较短的时间内出现在范围内,因此程序员意外泄漏的可能性可能较小。


总是?您的意思是“总是”,如“固定的Valgrind消息如何使OpenSSL在无用的旁边” marc.info/?t=114651088900003&r=1&w=2吗?或者,您是说另一个,“几乎总是”一个吗?
JensG 2014年

1
我可以想到三种允许未初始化变量且没有错误的语言,其中一种用于语言目的。
DougM 2014年

我会对细节感兴趣。在这些情况下,我会怀疑变量不是真正未初始化的,而是由程序员直接在声明站点进行初始化的。或者在取消引用之前通过某种间接方式分配它们。
Karl Bielefeldt 2014年

5

初始化,意味着初始值很重要。如果初始值很重要,那么可以,显然,您必须确保已将其初始化。如果没关系,则意味着它将在以后初始化。

不必要的初始化会浪费CPU周期。尽管这些浪费的周期在某些程序中可能无关紧要,但在其他程序中,每个周期都很重要,因为速度是最主要的问题。因此,了解一个人的性能目标以及是否需要初始化变量非常重要。

内存泄漏是一个完全不同的问题,通常涉及一个内存分配器函数来发布并稍后回收内存块。想想一个邮局。你去找一个邮箱。他们给你一个。您要求另一个。他们给你另一个。规则是,使用完邮箱后,您需要将其退还。如果您忘了把它还给他们,他们仍然会认为您拥有它,并且该盒子不能被其他任何人重复使用。因此,有一大堆内存被占用并且未被使用,这就是所谓的内存泄漏。如果您在某个时候一直要求输入框,则将耗尽内存。我已经简化了这一点,但这是基本思想。


-1是您在此上下文中重新定义初始化的含义。
Pieter B

@Pieter B,我不明白您的评论。请,如果您愿意,请说一下我的情况,“重新定义初始化在这种情况下的含义”。谢谢你
椭圆视图

阅读您自己的句子,这是循环的推理:“初始化,意味着初始值很重要。如果初始值很重要,那么是的,您必须确保已将其初始化。如果没关系,则意味着它将得到稍后初始化。”
Pieter B

@Pieter B,有些人将其初始化为一般规则,而不是出于编程原因,即,他们初始化初始值是否重要。这不是OQ的核心吗:初始化变量有多重要?无论如何,您已经在这里被投票了。
椭圆视图

2

正如其他人所说,这取决于语言。但是,我将演示有关初始化变量的Java(和有效Java)想法。这些应该可用于许多其他高级语言。

常量和类变量

staticJava 标记的类变量就像常量。这些变量通常应为最终变量,并在定义后使用=类初始化程序块或在其内部直接进行初始化static { // initialize here }

领域

与许多高级语言和脚本语言一样,将自动为字段分配默认值。对于数字,char这将是零值。对于Strings和其他对象,它将为null。现在null很危险,应谨慎使用。因此,应尽快将这些字段设置为有效值。构造函数通常是一个理想的选择。为了确保在构造函数中设置变量,并且以后不更改变量,可以用final关键字标记它们。

尝试抵制null用作某种标志或特殊值的冲动。最好包括一个特定的字段来保持状态。一个名称state使用State枚举值的字段将是一个不错的选择。

方法参数

因为调用者将看不到参数值的更改(它是引用对象还是诸如整数等基本类型的引用),所以应将参数标记为final。这意味着变量本身的值不能更改。请注意,可变对象实例的值可以更改,但是引用不能更改以指向其他对象null

局部变量

局部变量不会自动初始化;必须先初始化它们,然后才能使用它们的值。确保变量已初始化的一种方法是直接将它们初始化为某种默认值。然而,这是你应该不会做。大多数情况下,默认值不是您期望的值。

最好仅在需要该变量的位置精确地定义该变量。如果变量仅取一个值(对于良好代码中的大多数变量而言都是正确的),则可以标记该变量final。这样可以确保为局部变量分配的恰好是一次,而不是零次或两次。一个例子:

public static doMethod(final int x) {
    final int y; // no assignment yet, it's final so it *must* be assigned
    if (x < 0) {
        y = 0;
    } else if (x > 0) {
        y = x;
    } else {
        // do nothing <- error, y not assigned if x = 0
        // throwing an exception here is acceptable though
    }
}

请注意,如果变量在使用前仍未初始化,许多语言都会警告您。查看语言规范和论坛,看看是否不必担心。


1

初始化变量没有问题。

问题仅在于当您读取尚未写入的变量时。

根据编译器和/或变量的类型,在应用程序启动时执行初始化。或不。

通常不使用自动初始化。


0

初始化变量(隐式或显式)至关重要。不初始化变量总是一个错误(但是,它们可能会隐式初始化。请参见下文)。像C#编译器这样的现代编译器(例如)将其视为错误,并且不允许您执行代码。未初始化的变量就是毫无用处且有害的。除非要创建一个随机数生成器,否则您期望通过一段代码来产生确定性和可复制的结果。这只有在开始使用初始化变量时才能实现。

真正有趣的问题是变量是自动初始化还是您必须手动进行初始化。这取决于所使用的语言。例如,在C#中,字段(即类级别的“变量”)始终会自动初始化为该变量类型的默认值default(T)。该值对应于由全零组成的位模式。这是语言规范的一部分,而不仅仅是语言实现的技术细节。因此,您可以放心地依靠它。如果(且仅)在语言规范中指出隐式初始化变量,则不要显式初始化变量是安全的。如果需要另一个值,则必须显式初始化变量。然而; 在C#中,局部变量(即在方法中声明的变量)不会自动初始化,因此您必须始终显式初始化变量。


2
这不是C#特定的问题。
DougM 2014年

@DougM:我知道。这不是特定于C#的答案,我只是以C#为例。
Olivier Jacot-Descombes 2014年

并非所有语言都要求变量被显式初始化。您的“不初始化总是错误”声明是错误的,并且不会为当前的问题增加任何清晰度。您可能需要修改答案。
DougM 2014年

@DougM:您是否监督过我的句子“真正有趣的问题是变量是自动初始化还是必须手动进行初始化”。
Olivier Jacot-Descombes 2014年

您的意思是埋在段落中间一半的那个?是。您应该使它更加突出,并为您的“始终”声明添加限定词。
DougM 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.