为什么可以声明一个没有初始值的变量?

7
我正在阅读Gilles Dowek的《编程语言原理》:
他提到,在不给变量赋初值的情况下声明变量是可能的,但我们必须小心不要使用未赋初始值且未被赋值的变量,否则会产生错误。
需要注意的是,该书作者提到在Java中可以声明未被赋初值的变量。
那么为什么这种变量声明是有效的?我什么时候会用到它呢?

我相信答案因语言而异。 - Simon Forsberg
是的,作者在书中提到这个特性在Java中是有效的,我会编辑帖子。 - Red Banana
1
好问题。我想知道背后的原理是什么? - xagyg
8个回答

8
例如,您可以拥有像这样的东西:
int i;
if (first_condition)
    i = 1;
elseif (second_condition)
    i = 2;
else
    i = 0;

如果想在后面调用变量,需要在if语句之外先声明变量,但在if条件语句内部设置变量的值。


8
有许多不同的原因,导致使用许多不同的编程语言。 内存
当你声明一个变量时,你需要一些内存来保存它。这涉及到请求操作系统内核或某种监控程序来跟踪内存。简而言之,这可能是一个昂贵的操作。因此,在许多情况下,希望一次性分配对象所需的所有内存,然后稍后再分配任何需要分配的值。这样可以提高程序在关键部分的性能。这种用例很常见,允许声明而不初始化。然而,良好的实践要求在所有其他情况下都应该初始化变量同时进行分配。
把内存分配想象成一个官僚机构。太多的纸张工作。如果您知道将来要使用大量内存,可以一次性请求大量内存,而不是每次都向内核请求。 昂贵的初始化
这一点与上述点非常相似。假设您有一个100万乘以100万的数组。初始化这样的数组是一个昂贵的过程。这样使用默认值会很愚蠢,因此有一个这样的特性,即分配内存,然后根据需要使用它。
这就像你购买了大量的乐高积木来构建东西,但是你想要按照默认的蜘蛛侠形状购买它们。当你无论如何都会在稍后重新塑造它们时,店主或你自己都会费力地让它们成为蜘蛛侠形状。

3
如果你真正关注变量声明和初始化时发生的情况(即分配初始值),你会发现在机器指令级别或 Java 的字节码中,这两个步骤都需要使用大量的计算能力。
1. 声明变量意味着分配内存、创建所需类型的实例等等。 2. 然后初始化内存位置,再次需要更多的处理来将默认值移动到分配的内存位置,例如如果默认值为 0,则用 0 填充它。(注意:当随机内存位置分配给变量时,该内存可能包含由上一个值留下的任何模式)
因此,如果用户不知情地在未给变量赋予其类型的可接受值之前使用变量,则如果存在的值不正确,则可能会出现错误。
因此,如果一种语言强制您在声明时初始化变量(或自行执行此操作),则可以减少以后出现错误的机会,但这可能是在做您实际上不想要的事情时浪费处理能力。
另一方面,如果它允许您声明未初始化的变量,则会给您控制权,并且可能会为您节省一些计算能力,但也会增加出错的可能性。 (假设您有一个场景,其中变量的初始值取决于其他条件,将考虑并相应地分配变量。在这种情况下,在声明时初始化变量可能只是浪费处理能力)。
语言决定他们想要采取哪条路,大多数情况下取决于他们认为自己的力量所在。
如果它涉及给程序员一个机会来控制并创建高度优化的程序,则通常允许声明未初始化的变量,以及更多其他内容。
但如果该语言旨在强制程序员编写更加无误的程序,则会采取另一种方法。

2
如果Java要求类对象的字段必须在读取之前始终被写入,那么需要满足以下条件之一:
1. 类对象的构造函数必须写入该对象的所有字段,包括那些代码不会再次写入但却需要读取的字段;这既难看又低效。
2. 每个字段必须能够保存一个“从未被写入”的值,该值不同于可以写入它的任何其他值。
3. 编译器必须解决停机问题,以确定是否可以在未被写入的情况下读取字段。
4. 语言必须接受用户在未写入字段的情况下读取字段的可能性。
在这些可能的选择中,第四种方法最为可行。为避免未定义的行为,Java定义了读取从未被写入的字段的行为:它将包含其类型的默认值。

只有实例字段具有默认值。尝试在未初始化的情况下使用本地字段将导致编译错误。实例字段不需要初始化的事实更多是编译器简化而不是良好设计。这个答案解释得很好:https://dev59.com/eHI_5IYBdhLWcg3wBuRs - Jeffrey Blattman
也许我应该说“类实例”而不是“类对象”,但我认为那个问题的答案没有考虑到如果语言要求编写实例字段将需要什么。允许编译器生成代码而无需解决停机问题或为每个字段保留“从未编写过”的值可能是一种“简化”,但我更愿意把它看作是简单的实用性。 - supercat

1

在验证脚本中,您可以声明一个空变量实例。

$error='';
if(empty($_POST['first_name']{
$error=$error."You did not enter a name";

}

就像这样,但我只会在紧随其后重新声明代码的情况下使用它,如上所述,以便它不会“被遗失”。


1

我对c++不熟悉,但我认为这些信息可能也有帮助。

来自:使用C++进行编程:原理与实践

C++为什么提供声明和定义两种方式?声明/定义的区别反映了我们需要使用某些东西(接口)和使其正常工作所需的东西(实现)之间的基本区别。对于变量,声明提供类型,但只有定义提供对象(内存)。对于函数,声明再次提供类型(参数类型加返回类型),但只有定义提供函数体(可执行语句)。请注意,函数体作为程序的一部分存储在内存中,因此可以说函数和变量定义消耗内存,而声明则不会。


1

好的,这里有一个例子。我目前正在设置全局值,并将其设置为PDF表单上的持久值。对于此应用程序,我希望将这些值用作数据存储,就像文本字段一样。虽然不是最好的方法,但对于这个例子来说它可行。与其在论坛中填充数百个隐藏的文本字段,我可以将这些值设置为持久的全局变量。

如果我的初始声明将变量的值设置为0“”,那么它将不会在会话之间保留数据。论坛的初始化将重置该值为“nothing”。就像脚本一样,论坛按顺序运行。它将首先识别文档中的所有脚本,然后再为变量分配值。因此,有必要声明变量而不赋值,以便初始化不会替换这些值。

总的来说,在代码开头声明所有变量也是很好的习惯。虽然可以在使用时声明它们,但我发现当向脚本添加新元素时,我必须上下移动代码行以保持代码整洁。如果变量在使用时声明,那么在声明前使用变量也变得非常容易,但这是行不通的。

另外

在声明变量时赋值是高效的,但不要使用通用占位符值。在大多数脚本中,int并不像写成那样简单。更常见的情况是调用数据,例如 this.getField("TextBox3").value; 或其他某些行代码。通过分配诸如""0true之类的通用值,即使未正确利用该值,函数也可以正常工作。如果不分配值,则如果未使用变量,则函数将失败,从而使您能够缩小问题解决的可能性。如果在使用x之前,int x;仍然为NaN,则您的数据收集和验证可能是问题的源头。 总的来说,请在顶部声明所有值,并避免设置通用值,除非必须这样做。如果您稍后要定义x的值,则不要将其设置为int x = 0;

0

类似于:

//declare without giving value.
int i;

//set value
i=9;

1
这不是我的问题,我的问题是:如果变量会出错,为什么可以声明一个没有初始值的变量? - Red Banana
不是所有的东西都会出错...比如 int i; 会给出0。但如果你做 User user; 然后稍后 String s = user.address; 它会给你错误提示。 - user903772
本地变量不会被初始化为任何值,它们是未定义的... "本地变量略有不同;编译器永远不会将默认值分配给未初始化的本地变量。如果您无法在声明时初始化本地变量,请确保在尝试使用它之前为其赋值。访问未初始化的本地变量将导致编译时错误。" - xagyg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接