2012-06-25 98 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++的哲學是編譯任何可能在語法上編譯的東西。語義錯誤通常只會發出警告,並且只有在啓用此類警告時纔會發出警告。

相關問題