【问题标题】:C++ default value initialization changes with input-output streamsC++ 默认值初始化随输入输出流而变化
【发布时间】:2021-11-22 13:05:49
【问题描述】:

我正在研究两个代码段的有趣行为,这给了我意想不到的行为。

描述行为

代码段1:

#include <iostream>

using namespace std;

int main()
{
    long long n;
    long long m;
    
    // cin >> n;

    cout << m;

    return 0;
}

当我运行它时(例如在https://www.onlinegdb.com/online_c++_compiler 中),控制台中打印的值(变量m 中的值)为0。这是预期的并且对应于long long 类型具有默认初始化为 0(零初始化)。

代码段2:

#include <iostream>

using namespace std;

int main()
{
    long long n;
    long long m;
    
    cin >> n;

    cout << m;

    return 0;
}

通过取消注释带有 cin 的行并添加任何值,我得到的不是 0,而是一个不同的值,具体取决于我为 n 输入的数字。这种行为的其他特征是:

  1. 对于n 的不同输入值,我们在m 中得到不同的值。
  2. 每次为n 提供相同的值时,我们都会在m 中获得相同的值。
  3. 定义nm 静态 会产生预期的行为,即m 的值为0。
  4. 如果mnints,同样的行为仍然存在

因此,意外行为是 m 没有默认初始化为 0。

讨论

我的解释

  1. 未注释的行 cin &gt;&gt; n 创建了一个新线程以供执行。
  2. 新线程创建一个新堆栈,所有局部变量都复制到该堆栈中。
  3. 由于m没有在原栈中初始化,它携带的值在新栈中是不确定的。这取决于当前堆栈的状态。但是堆栈的状态取决于原始复制堆栈的状态,而原始复制堆栈的状态又取决于输入 n 的值。

问题

我的解释与特征 3 是一致的。不过,我不知道如何使用单个线程来检查这一点。此外,原因可能不仅仅与堆栈相关,即使只有一个线程,行为仍然存在。你怎么看?

【问题讨论】:

  • “这是预期的,并且对应于 long long 类型的默认初始化为 0(零初始化)” - 不,它没有。如果要对原语进行零初始化,则需要明确指定它。否则,该值是不确定的。
  • 这是预期的,并且对应于 long long 类型的默认初始化为 0(零初始化)的事实。 这是错误的。基本类型的默认初始化没有初始化。另一方面,值初始化将对基本类型进行零初始化。
  • 在您对cin &gt;&gt; n 创建一个新的执行线程的猜测中(它肯定不会),您顺便提到“m 未在原始线程中初始化”。这实际上是正确的,但与您之前的误解相矛盾,即m 的默认初始化应该将0 分配给它。
  • 尝试将mn 的定义移出main。我想你会得到你期望的零初始化
  • 感谢您的回答和澄清! StoryTeller 和 NathanOliver - 谢谢 - 我现在可以在 difference between default initialization and value initialization 上阅读。内森皮尔森 - 好点!谢谢!我需要重新整理我的线程。蒂姆 - 我希望能深入了解意外行为,而不是避免它。不过,我认为您是正确的,因为在方法中声明“静态”可以避免意外行为。

标签: c++ initialization


【解决方案1】:

C++ 初始化非常复杂。但是,这种情况相当简单(除了术语混淆;)。你是对的,mn 都是默认初始化的。不过,在那之后,你的解释就很不对劲了。它以

开头

这是意料之中的,并且对应于 long long 类型默认初始化为 0(零初始化)的事实。

long long int 的默认初始化是零初始化。实际上它根本没有初始化。来自 cppreference:

默认初始化在三种情况下执行:

  1. 当声明具有自动、静态或线程本地存储持续时间的变量时没有初始化器;

[...]

默认初始化的效果是:

  • 如果 T 是非 POD(C++11 之前)类类型,[...long long 不是类类型...]
  • 如果 T 是数组类型,[...long long不是数组类型...]
  • 否则,不会执行初始化:具有自动存储持续时间的对象(及其子对象)包含不确定的值。

读取不确定的值是未定义的行为。添加或删除看似不相关的代码行会更改输出是未定义行为的典型影响。你的代码的输出可以是任何东西。它是未定义的。

【讨论】:

  • 感谢您的澄清,@463035818_is_not_a_number !在你的回答之后我还有另一个问题:在你的最后一段中,你说未定义的行为受到添加或删除看似不相关的代码行的影响,并且我的代码的输出可以是任何东西。尽管如此,每次我输入相同的“n”时,我都会在“m”中得到相同的值。那么究竟什么是“未定义”?它似乎不是一个随机值。
  • @Louie_the_unsolver 见这里:en.cppreference.com/w/cpp/language/ub。 “编译器不需要诊断未定义的行为(尽管诊断了许多简单的情况),并且编译后的程序不需要做任何有意义的事情。”你可能会看到一致的结果,输出不是随机的,关键是不能保证你得到什么输出。使用不同的编译器,在不同的操作系统上,使用不同的编译器设置等,您可能会得到完全不同的输出
猜你喜欢
  • 1970-01-01
  • 2020-02-08
  • 1970-01-01
  • 2013-01-17
  • 1970-01-01
  • 2014-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多