允许变量未初始化有什么好处?

8

在许多编程语言中,您可以声明一个变量并在初始化之前使用它。

例如,在C++中,您可以编写以下代码:

int x;
cout << x;

当然,这样做会返回不可预测的结果(除非您知道程序如何映射内存),但我的问题是,为什么编译器允许这种行为?
是否有一些应用程序或效率,可以通过使用未初始化的内存来实现?
编辑:我想到了一个点,即将初始化留给用户可以减少对具有有限寿命(写入循环)的内存介质的写入。这只是在上述“性能”标题下的一个具体示例。谢谢。
8个回答

13

我认为这只是语言早期版本的遗留问题(我的想法可能是错误的,可以问问我的妻子)。

早期的C语言版本不允许在函数中随意声明变量,它们必须位于函数的开头(或者可能是块的开头,我很难准确回忆起来,因为现在我很少这样做)。

此外,您有一个可以理解的愿望:仅在您知道变量应该是什么时才设置变量。如果下一步要做的事情只是简单地覆盖该值,那么将变量初始化为某些东西是没有意义的(这就是性能方面的考虑)。

这就是为什么需要允许未初始化变量的原因,尽管在初始化之前不应该使用它们,而且好的编译器会发出警告来提醒您。

在C ++(和后来的C版本)中,您可以在函数中任何位置创建变量,因此您确实应该同时创建和初始化它。但是在早期版本中这是不可能的。您必须使用类似以下的代码:

int fn(void) {
    int x, y;
    /* Do some stuff to set y */
    x = y + 2;
    /* Do some more stuff */
}

现在,我会选择:

int fn(void) {
    int y;
    /* Do some stuff to set y */
    int x = y + 2;
    /* Do some more stuff */
}

+1 作为多年的 C/C++ 程序员,我也同意这一点。我本来也想回复这样的内容,你比我先发了 :) 尽可能靠近使用变量的主要代码逻辑,用有意义的值初始化变量是明智的 - 距离度量。 - Fadrian Sudaman
1
+1 对于现代编译器,它们将会给你警告。只需将警告级别设为最高,并将所有警告转换为错误,这就像强制要求人们在使用之前进行初始化一样。仅仅因为人类往往懒惰,并不意味着我们不能通过聪明的编译器进行补偿 :-) 愿编译器伟大的支配者万岁。 - Martin York
实际上,我可能会创建一个替代函数,这样我就可以编写 int y = alternate() 而不必担心未初始化的 y。如果我们能够像 Python 一样声明变量就好了:if test: y = 3; else: y = 4,这样可以有效地防止未初始化的变量而不必在各处创建单行函数。 - Matthieu M.

5

编程中最古老的借口:它可以提高性能!

编辑:我看了你的评论并同意——多年前,性能的重点在于CPU周期数。我的第一个C编译器是传统的C(ANSI C之前那个版本),它允许编译各种可怕的代码。在这个现代化的时代,性能与客户投诉数量有关。正如我告诉我们雇佣的新毕业生们一样:“我不关心程序多快能给出错误答案。”使用现代编译器和开发的所有工具,写少量bug,每个人都能按时下班。


2
不声明变量会更提高性能吗?这样,内存甚至不需要为变量分配。任何引用此类代码的性能的人都是错的。 - Thomas Owens
1
关键词是“最古老的”。40年前,当C语言正在开发时,“浪费”一两个指令来自动初始化变量可能真的很重要。 - dan04
浪费一条指令可能是有价值的,但就性能而言,多出来的那几条指令会使应用程序的性能变差。 - Thomas Owens
2
C++程序不仅适用于功能强大的台式机,还可用于小型微控制器(好吧,C语言可以)。编写微控制器程序的人不想放弃额外的指令(这不仅涉及时间,还涉及空间)。为什么他们要付出初始化成本呢?他们知道使用未初始化内存的相关问题,所以为什么要强制所有人都承担这个成本,只是为了保护一些缺乏经验的程序员呢?我认为他们已经得到了保护;现代编译器总会警告您此问题。 - Martin York
@martin - 我同意。我关于使用现代工具和编译器的观点是它们可以配置以检测潜在问题并抛出警告,然后开发人员可以做出决策。 - jqa
如果你编写了一堆不安全的代码,你可以推动非常老旧的硬件显示比它应该显示的更多颜色。看看Demo场景是怎么做的。 - ZeroPhase

4

一些API的设计是通过传递变量来返回数据,例如:

bool ok;
int x = convert_to_int(some_string, &ok);

这个函数可能会设置“ok”的值,因此初始化是浪费的。

(我不赞成这种API风格。)


1
有关此的“真实世界”用法,请参见Qt的QString类:http://doc.qt.nokia.com/4.6/qstring.html#toInt作为示例。请注意bool * ok。 - sivabudh
它取决于函数参数是否为OUT类型或INOUT类型。 - YeenFei
2
与标准输入流相同:bool ok; std::cin >> ok; - rafak

2
简而言之,对于较为复杂的情况,编译器可能无法确定变量是否在初始化前被使用。
例如:
int x;
if (external_function() == 2) {
   x = 42;
} else if (another_function() == 3) {
   x = 49;
}
yet_another_function( &x );
cout << x;   // Is this a use-before-definition?

好的编译器会在能够发现可能使用未初始化错误时给出警告信息,但对于复杂情况 - 特别是涉及多个编译单元的情况 - 编译器无法判断。
至于语言是否应该允许未初始化变量的概念,那是另一回事。C# 稍微有点不同,它将每个变量定义为使用默认值进行初始化。大多数语言(如 C++/C/BCPL/FORTRAN/Assembler/...)都由程序员决定初始化是否合适。好的编译器有时可以发现不必要的初始化并消除它们,但这并非必然。为更晦涩的硬件编写的编译器往往对优化投入的精力较少(这是编译器编写的难点),因此针对此类硬件的语言 tend not to require unnecessary code generation.

1

也许在某些情况下,将内存保持未初始化状态直到需要它可能更快(例如,如果您在使用变量之前从函数返回)。我通常会初始化所有内容,我怀疑这不会对性能产生任何真正的影响。编译器肯定有自己的优化方式来消除无用的初始化。


1

有些语言对某些变量类型有默认值。话虽如此,我怀疑在任何语言中都没有不显式初始化的性能优势。然而,缺点是:

  • 必须初始化的可能性,如果未完成,则会导致崩溃
  • 意外的值
  • 其他程序员缺乏清晰和目的

我的建议是始终初始化您的变量,并且一致性将为自己付出。


0

for循环的样式

int i;
for(i=0;i<something;++i){
    .......
}
do something with i

而且您更喜欢for循环看起来像for(init;condition;inc)

这里有一个绝对必要的

bool b;
do{
    ....
    b = g();
    ....
}while(!b);

具有长嵌套名称的水平屏幕房地产

更长寿的作用域,以提高调试可见性

非常偶尔的性能问题


0

根据变量的大小,在性能名称中未初始化值可能被视为微观优化。与广泛存在的软件类型相比,相对较少的程序会受到额外两三个周期加载双精度浮点数的负面影响;然而,假设变量非常大,则推迟初始化直到明确需要初始化可能是一个好主意。


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