2012-06-25 83 views
25

可能重复:
int var = 1; void main() { int i = i; }为什么'int i = i;'法律?

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

int main() { 
    int i = i; 
} 
+7

对我来说它不是非法的,它只是滥用符号。 –

+8

它的评估为(i)'int i'(ii)'i = i'按照这个顺序 –

+0

我认为出于同样的原因,只是'int i;'没有分配'i'是合法的。 – asmeurer

回答

42

编辑:这句法法律,但结果不确定的行为,如果你使用x

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

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

积分史蒂夫·杰索普,谁挖出了报价:

4.1/1,左值到右值转换

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

+0

该参数是循环变量,其值未定义的变量当然不是非法的。将它与只是普通的'int i;'相比较,这也会使'i'具有未定义的值。 – unwind

+0

@nhahtdh,这并不意味着它是合法的。 –

+0

Luchian,它编译,规范中没有任何东西不允许它。 – OmnipotentEntity

7

你可以让g++警告你该用例-Winit-self(与-Wuninitialized一起),如果你将警告视为错误的,它应该满足你的痒。

这种使用复制构造函数进行自我初始化的技术有时用于抑制全局对象的默认构造函数/构造函数的执行。如果全局对象的默认构造函数仅用于0初始化对象,但在构造函数执行之前已使用该对象,则这可能是必需的。作为C的返回,全局变量0在程序启动时初始化,然后C++运行时开始执行全局构造函数。对于那些已经执行的已定义构造函数仅用于0以外的狭窄情况,自我初始化不会造成任何伤害。

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

+0

尽管如此,它似乎没有为'-Winit-self'生成任何警告。 – nhahtdh

+0

@nhahtdh:谢谢,我更新了这篇文章。问候 – jxh

+0

如果对象有一个用户定义的类型,并且构造函数通过引用接受实例,并且从不取消对它的引用(在构造函数中),那么代码是合法的。 (例如,用户定义的构造函数可以保存地址。) –

14

它是由语法允许的理由是,有一些奇怪的情况下,你可以设想要在自己的初始化指针或引用使用变量:

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;确实使用了未初始化的值)。

您的编译器可能有助于识别未初始化值的使用情况,但该标准不需要它。

2

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

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

这不是一个错误,因为编译器可以理解它,因为它可以生成明确无误地执行源代码指定的代码,如果它在语义上可疑,则为七。 C和C++的哲学是编译任何可能在语法上编译的东西。语义错误通常只会发出警告,并且只有在启用此类警告时才会发出警告。

相关问题