为什么 'int i = i;' 是合法的?

31

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

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

int main() {
  int i = i;
}

7
对我来说,这不是非法的,只是符号滥用。 - me_digvijay
8
它按顺序评估为(i)int i(ii)i = i - Salman A
我认为与仅声明int i;而从未对i进行分配的原因相同,这是合法的。 - asmeurer
@asmeurer 但是使用 i 是非法的。 - Luchian Grigore
4个回答

47
编辑:从句法上讲是合法的,但如果使用x会导致未定义的行为。
这是不合法的,因为您正在将一个未初始化的变量与另一个(或同一)未初始化的变量进行赋值。仅仅因为它可以编译并不意味着它是合法的。是有效的C++语法,但不是合法的。
赋值运算符的右侧必须在赋值时完全计算。在这种情况下,那就是未初始化的i
感谢Steve Jessop挖掘出这句话: 4.1 / 1,左值到右值转换
  

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


2
@LuchianGrigore:从法律上讲合法吗?我觉得这样做完全是错误的,但编译器却可以顺利编译。 - nhahtdh
3
这句话的意思是“这是100%正确的——代码是非法的”。如果他提到了关键词“未定义的行为”,答案可能会更好,但他已经涵盖了所有重要的点:语法有效,但不合法;在初始化之前计算i等。 - James Kanze
8
在标准定义的意义上,还有什么其他的呢?无论它能否编译都与合法性无关。否则我们就没有未定义行为了。如果你不明白,请停止(下)投票! - Konrad Rudolph
9
如果有帮助的话:4.1/1,左值到右值转换,“如果对象未初始化,则需要此转换的程序具有未定义的行为”。您不能读取未初始化的对象,因此除非您认为在其自己的初始化程序中读取i时它不是未初始化的,否则我认为关于这种情况标准所说的内容没有太多争议。 - Steve Jessop
7
我认为你必须澄清“有效”和“合法”这两个词的确切含义,因为很多人似乎混淆了这两个词。 - Salman A
显示剩余12条评论

17

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

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++的答案。谢谢。 - Roddy

8
您可以使用-Winit-self(与-Wuninitialized一起使用)来让g++警告您此用例。如果您将警告视为错误,它应该可以满足您的需求。
有时会使用这种使用复制构造函数进行自初始化的技术来抑制全局对象的默认构造函数/初始化程序。如果全局对象的默认构造函数只是将对象初始化为0,但在构造函数执行之前就已经使用了对象,则可能需要这样做。作为对C的回归,全局在程序启动时初始化为0,然后C++运行时开始执行全局构造函数。对于那些定义的构造函数仅将对象初始化为0的特定情况,自初始化不会造成任何伤害。
在一般情况下,复制构造函数的自初始化是不好的实践,因为它通常会导致使用未初始化变量引起的问题(即未定义行为)。在OP问题中的特定示例中,imain中的本地变量,因此未初始化。读取未初始化变量的结果始终是未定义的行为。

不过,对我来说,似乎没有使用“-Winit-self”会生成任何警告。 - nhahtdh
如果对象具有用户定义类型,并且构造函数通过引用获取实例,并且在构造函数中从未对其进行解引用,则代码是合法的。(例如,用户定义的构造函数可以保存地址。) - James Kanze
@JamesKanze:是的,我看了你有趣的例子。但是,我只是在谈论复制构造函数。谢谢和问候。 - jxh

3
你可以使用任何先前声明的变量作为另一个变量的初始值。
在这种情况下,一旦编译器解析了“int i”,它就会将其添加到符号表中,因此当它看到“= i”初始化程序时,该符号可以从前面的声明中解析。
这不是错误,因为编译器可以理解它,即使它在语义上有问题,它也可以生成明确执行源代码指定操作的代码。 C和C ++的哲学是编译可能在语法上被编译的任何内容。 语义错误通常只发出警告,而且只有在启用此类警告时才会发出。

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