2012-06-25 15 views
25

Powiel możliwe:
int var = 1; void main() { int i = i; }Dlaczego "int i = i;" prawny?

Poniższy kod może przejść kompilacji zarówno pod g ++ i Visual C++. Dlaczego jest legalny? Wygląda nierozsądnie i może powodować ukryte błędy.

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

Dla mnie to nie jest nielegalne, to tylko nadużycie notacji. –

+8

Wylicza jako (i) 'int i' (ii)' i = i' w tej kolejności –

+0

Myślę, że z tego samego powodu, że po prostu 'int i;' bez przypisywania 'i' jest legalne. – asmeurer

Odpowiedz

42

EDIT: To składniowo prawną, ale wyniki w niezdefiniowanej zachowań jeśli używasz x.

To jest legalne, ponieważ przypisujesz niezainicjowaną zmienną do innej (dobrze, tej samej) niezainicjowanej zmiennej. To, że go kompiluje, nie oznacza, że ​​jest legalne. Jest to poprawna składnia C++, tak, ale nie legalna.

Prawa strona operatora przypisania musi zostać w pełni oceniona w momencie przypisania. W tym przypadku jest to i, który nie został zainicjowany.

kuponów do Steve Jessop, którzy odkopali Cytat:

4,1/1, konwersja lwartość-to-RValue

[...], jeśli obiekt jest niezainicjowany, program to wymaga tej konwersji ma niezdefiniowane zachowanie.

+0

Ten argument jest kolisty, a posiadanie zmiennej, której wartość jest niezdefiniowana, z pewnością nie jest nielegalne. Porównaj to do zwykłego 'int i;', które również pozostawia 'i' z nieokreśloną wartością. – unwind

+0

@nhahtdh, co nie znaczy, że jest legalne. –

+0

Luchian, kompiluje i nie ma w nim nic, co by go uniemożliwiło. – OmnipotentEntity

7

Możesz pozwolić g++ ostrzec cię o tym przypadku korzystania z -Winit-self (w połączeniu z -Wuninitialized), a jeśli traktować ostrzeżenia jako błędy, powinna ona spełniać swoje swędzenie.

Ta technika użycia konstruktora kopiowania do samoczynnego zainicjowania jest czasami używana do powstrzymania wykonywania domyślnego konstruktora/inicjalizatora obiektu globalnego. Może to być konieczne, jeśli domyślnym konstruktorem obiektu globalnego jest tylko 0 zainicjować obiekt, ale obiekt został użyty przed wykonaniem konstruktora. Jako powrót do C globals są 0 zainicjalizowane przy starcie programu, a następnie środowisko wykonawcze C++ zaczyna wykonywać globalne konstruktory. W przypadku wąskich przypadków, w których zdefiniowany konstruktor, który byłby wykonywany tylko w celu usunięcia obiektu, samoinicjalizacja nie wyrządzi szkody.

W ogólnym przypadku samoinicjalizacja konstruktora kopiowania jest złą praktyką, ponieważ generalnie powodowałoby to ten sam rodzaj problemów, które powodowałyby użycie niezainicjowanej zmiennej (czyli niezdefiniowane zachowanie). W konkretnym przykładzie w pytaniu OP, i jest lokalne dla main i dlatego jest niezainicjowane. Wynik odczytu niezainicjowanej zmiennej jest zawsze niezdefiniowanym zachowaniem.

+0

Wydaje się jednak, że nie generuje dla mnie żadnego ostrzeżenia z "-Witit-self". – nhahtdh

+0

@nhahtdh: Dzięki, zaktualizowałem post. Pozdrawiam – jxh

+0

_If_ obiekt ma typ zdefiniowany przez użytkownika, a konstruktor pobiera instancję przez odniesienie i nigdy nie usuwa go (w konstruktorze), a następnie kod jest legalny. (Konstruktor zdefiniowany przez użytkownika może zapisać adres, na przykład.) –

14

Powodem jest to dozwolone przez składni jest to, że istnieją pewne dziwne przypadki, w których można chcieć użyć zmiennej przez wskaźnik lub odwołanie we własnym inicjatora:

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); 

Więc C++ pozwala odnosić się do obiekt w swoim własnym wyrażeniu inicjalizacyjnym (jego nazwa znajduje się w zakresie).Jak zawsze w C++, twoim problemem jest upewnienie się, że nie używasz niezainicjowanej wartości (twój przykład, int i = i; używa wartości niezainicjowanej).

Kompilator może pomóc w zidentyfikowaniu zastosowań niezainicjowanych wartości, ale standard tego nie wymaga.

2

Możesz użyć dowolnej wcześniej zadeklarowanej zmiennej jako inicjalizatora innej zmiennej.

W tym przypadku, gdy kompilator analizuje int i, dodaje to do tabeli symboli, więc gdy widzi inicjalizator = i, symbol może zostać usunięty z poprzedniej deklaracji.

Nie jest to błąd, ponieważ kompilator może mieć sens, ponieważ może wygenerować kod, który jednoznacznie wykonuje dokładnie to, co określa kod źródłowy, siedem, jeśli jest semantycznie podejrzany. Filozofią C i C++ jest kompilacja wszystkiego, co może być kompilowane syntaktycznie. Błędy semantyczne zwykle generują jedynie ostrzeżenia, a tylko wtedy, gdy takie ostrzeżenia są włączone.