【问题标题】:Why is 'int i = i;' legal? [duplicate]为什么'int i = i;'合法的? [复制]
【发布时间】:2012-06-25 09:02:32
【问题描述】:

可能重复:
int var = 1; void main() { int i = i; }

以下代码在g++和Visual C++下都可以通过编译。为什么是合法的?看起来不合理,可能会导致隐藏的bug。

int main() {
  int i = i;
}

【问题讨论】:

  • 对我来说这不是非法的,只是符号的滥用。
  • 按顺序计算为 (i) int i (ii) i = i
  • 出于同样的原因,我认为只是 int i; 而不分配 i 是合法的。
  • @asmeurer 但是使用i 是非法的。

标签: c++


【解决方案1】:

编辑:在语法上是合法的,但如果您使用x,则会导致未定义的行为。

这是合法的,因为您将一个未初始化的变量与另一个(嗯,相同的)未初始化的变量一起分配。仅仅因为它编译并不意味着它是合法的。这是有效的 C++ 语法,是的,但不合法。

赋值运算符的右侧必须在赋值时进行全面评估。在这种情况下,它是i,它没有被初始化。

感谢史蒂夫·杰索普,他挖出了这句话:

4.1/1,左值到右值的转换

[...] 如果对象未初始化,则需要执行此操作的程序 转换具有未定义的行为。

【讨论】:

  • @LuchianGrigore:在什么意义上合法?不过,我认为这样做是完全错误的。但是编译器很高兴地编译它。
  • 这是 100% 正确的——代码是非法的。如果他提到关键字“未定义的行为”,答案可能会稍微好一些,但他涵盖了所有要点:有效的语法,但不合法; i 在初始化之前进行评估;等等
  • @nhahtdh 在标准中定义的意义上,还有什么? 它是否编译与合法性无关。否则我们将没有未定义的行为。 如果您不知道,请停止(拒绝)投票,人们!
  • 如果有帮助:4.1/1,左值到右值转换,“如果对象未初始化,则需要此转换的程序具有未定义的行为”。您无法读取未初始化的对象,因此除非您愿意争辩 i 在其自己的初始化程序中读取时并未未初始化,否则我认为标准对这种情况的说法没有太多争议。
  • 我认为你必须澄清你所说的 validlegal 到底是什么意思。很多人似乎混淆了这两个词。
【解决方案2】:

语法允许它的原因是,在某些奇怪的情况下,您可能想在自己的初始化程序中通过指针或引用来使用变量:

struct ThingManager {
    void *thing;
    ThingManager(void *thing) : thing(thing) {}
    void Speak() {
        if (thing == (void*)this) {
            std::cout << "I'm managing myself\n";
        } else {
            std::cout << "I'm managing " << thing << "\n";
        }
    }
};

ThingManager self_manager(&self_manager);
ThingManager other_manager(&self_manager);

因此,C++ 允许您在其自己的初始化表达式中引用一个对象(其名称在范围内)。然后和以往一样在 C++ 中,确保您实际上没有使用未初始化的值是您的问题(您的示例,int i = i; 确实使用了未初始化的值)。

您的编译器可能有助于识别未初始化值的用途,但标准并不要求这样做。

【讨论】:

  • 这是唯一能说明为什么它实际上需要是合法的 C++ 的答案。谢谢,
【解决方案3】:

您可以让g++ 使用-Winit-self(与-Wuninitialized 一起)警告您有关此用例的情况,如果您将警告视为错误,它应该可以满足您的需求。

这种使用复制构造函数进行自初始化的技术有时用于禁止执行全局对象的默认构造函数/初始化程序。如果全局对象的默认构造函数只是为了0 初始化对象,那么这可能是必要的,但对象在构造函数执行之前就被使用了。作为对 C 的回归,全局变量在程序启动时初始化 0,然后 C++ 运行时开始执行全局构造函数。对于那些定义的构造函数只执行0出对象的狭隘情况,自初始化不会造成任何伤害。

在一般情况下,复制构造函数自初始化是不好的做法,因为它通常会导致与使用未初始化变量相同的问题(即未定义的行为)。在 OP 问题的特定示例中,imain 的本地,因此未初始化。读取未初始化变量的结果总是未定义的行为。

【讨论】:

  • 不过,-Winit-self 对我来说似乎没有产生任何警告。
  • @nhahtdh:谢谢,我更新了帖子。问候
  • 如果对象具有用户定义的类型,并且构造函数通过引用获取实例,并且从不取消引用它(在构造函数中),那么代码是合法的。 (例如,用户定义的构造函数可以保存地址。)
  • @JamesKanze:是的,我读到了你有趣的例子。但是,我只是在谈论复制构造函数。谢谢和问候
【解决方案4】:

您可以使用任何先前声明的变量作为另一个变量的初始化器。

在这种情况下,只要编译器解析 int i,它就会将其添加到符号表中,因此当它看到 = i 初始化器时,可以从前面的声明中解析符号。

这不是错误,因为编译器可以理解它,因为它可以生成明确地完全按照源代码指定的代码,即使它在语义上是可疑的。 C 和 C++ 的哲学是编译任何可以在语法上编译的东西。语义错误通常只发出警告,并且只有在启用此类警告时才会发出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-06
    • 1970-01-01
    • 2017-10-23
    • 2020-11-25
    • 2013-03-24
    • 1970-01-01
    • 1970-01-01
    • 2015-06-17
    相关资源
    最近更新 更多