2015-07-22 18 views
23

I grał z jakimś składni i znaleźć jakieś dziwne zasady kompilatora, zastanawiałem się co rozumowanie jest dla tegoC vs C++ switch definicji zmiennej vs deklaracji

C nie będzie skompilować ten jednak C++ będzie:

switch (argc) { 
case 0: 
    int foo; 
    break; 
default: 
    break; 
} 

Zarówno C i C++ będzie skompilować ten:

switch (argc) { 
case 0: 
    ; int foo; 
    break; 
default: 
    break; 
} 

C skompiluje to, ale nie C++:

switch (argc) { 
case 0: 
    ; int foo = 0; 
    break; 
default: 
    break; 
} 

gcc -v jest gcc version 4.9.3 (MacPorts gcc49 4.9.3_0) jeśli to ma znaczenie. Zdaję sobie sprawę, rozwiązaniem jest owinąć zawartość case 0: z nawiasów, ale jestem bardziej zainteresowany w rozumowaniu błędów kompilacji

+5

[Ta odpowiedź] (http://stackoverflow.com/a/92730/962089) jest w pewnym sensie istotna. – chris

+2

Wygląda na to, że drugi przypadek nadal działa dla C++, jeśli ustawisz 'foo' w dodatkowej instrukcji. gcc po prostu narzeka na nieużywaną zmienną w [tym przykładzie] (http://coliru.stacked-crooked.com/a/ba80bc1da97d8212), a clang jest z nią w porządku. – jaggedSpire

+1

@chris, Dzięki, trzeci przypadek ma teraz sens (nie można przeskoczyć inicjalizacji w C++) – asimes

Odpowiedz

23
case 0: 
    int foo; 

Zarówno w C i C++ znakowany stwierdzenie jest etykieta następnie oświadczeniu. Jednak w C++ definicja instrukcji zawiera "deklaracje blokowe" (to jest deklaracje i definicje, które mogą pojawić się w bloku), podczas gdy w C nie ma (w C blok jest sekwencją "elementów blokowych", które są albo blokami deklaracje lub wyciągi - w C++ jest to ciąg instrukcji, które zawierają deklaracje blokowe).

case 0: 
    ; int foo; 

To działa, ponieważ ; jest (n pusty) oświadczenie zarówno C i C++, więc tutaj możemy rzeczywiście mieć etykietę następnie oświadczeniu.

case 0: 
    ; int foo = 0; 

Jak już wyjaśniono w komentarzach, to nie działa w C++, ponieważ C++ uniemożliwia przeskoczenie inicjalizacji.

+0

To jest dobra odpowiedź, ale trzeci przypadek jest nadal tym, który wprowadza mnie w błąd najbardziej. Jak przeskakuje inicjalizację? Dlaczego umieszczenie definicji w nawiasach klamrowych pozwala uniknąć przeskakiwania przez inicjalizację? – asimes

+1

@asimes, Gdy wstawisz nawiasy klamrowe, '{; int foo = 0;} 'jest instrukcją dla * oznaczonej instrukcji *. Kiedy tego nie robisz, 'int foo = 0;' jest częścią przełącznika, a nie jedną instrukcją dla sprawy, więc znajduje się w zasięgu poza kolejną etykietą, ale jej inicjalizacja jest pomijana. – chris

+0

@asimes Potencjalnie przeskakuje inicjalizację, ponieważ może przeskoczyć do domyślnego przypadku, w którym 'foo' będzie w zasięgu, ale nie zostanie zainicjalizowane. Jeśli dodasz nawiasy klamrowe, 'foo' nie znajduje się już w zasięgu w bloku domyślnym.Zasadniczo zasady "przeskakiwania przez inicjalizację" zabraniają przeskakiwania "w środku" zakresu zmiennej, która ma inicjator. Oznacza to, że możesz przejść do inicjalizacji lub po tym, jak zmienna wyszła poza zasięg, ale nie do żadnego miejsca pomiędzy tymi punktami. – sepp2k