为什么打印未初始化的变量时会看到奇怪的值?

Why do I see strange values when I print uninitialized variables?

提问人:cmuse 提问时间:11/24/2010 最后编辑:Donald Duckcmuse 更新时间:7/10/2019 访问量:9030

问:

在下面的代码中,该变量没有初始值,并打印了此变量。

int var;
cout << var << endl;

输出 : 2514932

double var;
cout << var << endl;

输出 : 1.23769E-307

我不明白这些输出数字。谁能向我解释一下?

C 变量 C++-FAQ 初始化

评论

3赞 Martin York 11/24/2010
编译此代码时。编译器是否不会生成大量有关未初始化变量的警告?如果将编译器设置为将警告视为错误(一个好主意),并且还提高了警告级别,则它不会编译,并且您将生成更安全的代码(警告通常是逻辑错误)。
0赞 cmuse 11/24/2010
是的,我在 Ubuntu 上使用 GCC 编译此代码。这不是任何警告。
2赞 wkl 11/24/2010
@cmuse - 使用 编译 将帮助您发现更多警告。一旦你使用该标志,这个问题就会弹出。-Wallgcc -Wall source.c -o myexecutable
3赞 Rob Kennedy 11/24/2010
你希望你的程序打印什么值?
0赞 cmuse 11/24/2010
@Rob肯尼迪 - 我不期望任何事情。我很好奇它是什么。

答:

4赞 wkl 11/24/2010 #1

当您这样做时:

int var;

您只声明了一个名为 的整数。您没有使用值对其进行初始化,因此无论在哪个位置,都将是垃圾数据。varvar

int var = 5;

将声明 var 并将其初始化为 5。

查看更多:http://en.wikipedia.org/wiki/Uninitialized_variable

1赞 Sam Miller 11/24/2010 #2

在任何一种情况下,您都没有初始化,因此您会得到垃圾输出。var

你做过吗

const int var(5);

它将使用以下值进行初始化5

3赞 Omnifarious 11/24/2010 #3

你得到的是编译器决定变量应该被解释为整数或双精度值的堆栈上的任何数据。每次程序运行时,它可能都是一样的,因为程序通常具有确定性。尽管在许多情况下,它最终会从程序的运行到运行中变得不相同。如果你稍微改变你的程序,或者让它在你得到该代码之前根据用户输入做出决定,你可能会也可能不会得到不同的数字。

基本上,您尚未初始化的变量的值是未指定的,并且可能绝对是任何值。那里有什么没有押韵或理由。使用未初始化的变量(从形式上讲)是未定义的行为,可能会导致各种奇怪的事情。

这样做通常是不好的做法。您希望程序以可预测的方式运行,而具有未初始化的变量是不可预测性的根源。请注意,它最强调的不是随机性的来源,而是不可预测性。如果你打开所有警告,大多数编译器都会抱怨这样的代码。

评论

0赞 Bruno Brant 11/24/2010
对不起,即使程序确定性地分配内存地址偏移量,它也不会相同,因为内存可能已被其他程序使用,或者程序的基址可能已更改。
0赞 Omnifarious 11/24/2010
@Bruno 布兰特 - 这是真的。在许多情况下,情况是相同的,而且很可能是 OP。但我应该用这个细节来更新我的答案。
35赞 GManNickG 11/24/2010 #4

简单地说,未初始化,读取未初始化的变量会导致未定义的行为var

所以不要这样做。在你这样做的那一刻,你的程序不再保证做你说的任何事情。


从形式上讲,“读取”一个值意味着对它执行左值到右值的转换。§4.1 规定“......如果对象未初始化,则需要此转换的程序具有未定义的行为。

实际上,这只是意味着该值是垃圾(毕竟,很容易看到读取 ,例如,只是得到随机位),但我们不能得出这样的结论,否则您将定义未定义的行为。int

对于一个真实的例子,请考虑:

#include <iostream>

const char* test()
{
    bool b; // uninitialized

    switch (b) // undefined behavior!
    {
    case false:
        return "false";      // garbage was zero (zero is false)
    case true: 
        return "true";       // garbage was non-zero (non-zero is true)
    default:
        return "impossible"; // options are exhausted, this must be impossible...
    }
}

int main()
{
    std::cout << test() << std::endl;
}

天真地,人们会得出结论(通过评论中的推理)这不应该打印;但是对于未定义的行为,一切皆有可能。使用 编译它。"impossible"g++ -02

评论

0赞 Omnifarious 11/24/2010
您确定结果是未定义的而不是未指定的吗?当然,取消引用未初始化的指针绝对应该是未定义的。但是,未初始化的变量不就是未指定什么值吗?
0赞 GManNickG 11/24/2010
@Omni:我已经在回答中澄清了。
1赞 Omnifarious 11/24/2010
+1 - 最出色的澄清。我有时会感到尴尬,因为我不知道我一开始不会做的事情的全部后果。:-)不过,我应该意识到,因为我在想,如果最终在其中出现垃圾,会发生什么,而这些垃圾通常不可能包含在其中,从而违反了该类型的不变量。证明这一点要容易得多,也更具戏剧性。doubledoubledoublebool
0赞 Lorenzo Pistone 11/24/2015
这是在 g++ 4.9 和 5.1 中打印的,它应该如何打印?"false""impossible"
0赞 GManNickG 11/24/2015
@LorenzoPistone:在旧版本的 g++ 上,这打印了“不可能”,我不太记得应用了什么优化(我认为它试图通过删除它可能认为不会被占用的分支来简化切换,并且可以假设 和 分支都没有因为 UB 而被占用),但它们似乎终于改变了。falsetrue
1赞 Sean 11/24/2010 #5

当您声明 var 时,它会在内存中分配一个位置。但是,默认情况下,该内存不会设置为任何内容,因此您可以拾取之前存在的任何内容。这将是一些没有意义的垃圾值。

在 C++ 中,成员变量和局部变量都是如此。但是,在 Java 和 C# 等语言中,成员变量会自动初始化为 0(对于数字类型),对于布尔值,对于引用,则自动初始化为 null。这不会针对局部变量执行,并且(至少在 C# 编译器中)如果尝试获取未初始化变量的值,则生成将失败。

2赞 Bruno Brant 11/24/2010 #6

在 C++ 中,当您声明变量时,编译器会为其分配一个内存地址。就是这样,没有进行清理。这主要是因为 C++(和 C)在构建时考虑了性能。C++ 不会花时间初始化地址,除非您明确告诉它这样做。

你所看到的所谓垃圾 是最后一个使用它的变量留在该地址的任何东西。

其他语言将为您初始化数据。事实上,在初始化变量之前,C# 不允许您使用该变量。这些语言被设计为安全,从某种意义上说,它不会让你编写错误使用未初始化地址的代码,导致程序崩溃,或者更糟糕的是,损坏你的数据。