2010-03-06 11 views
69

Rozważmy następujący kod:Jakie są oznaki inicjowania przecinków?

#include <iostream> 
using namespace std; 

int main() 
{ 
    int x, y, i; 
    cin >> x >> y >> i; 
    switch(i) { 
     case 1: 
      // int r = x + y; -- OK 
      int r = 1; // Failed to Compile 
      cout << r; 
      break; 
     case 2: 
      r = x - y; 
      cout << r; 
      break; 
    }; 
} 

G ++ narzeka crosses initialization of 'int r' pytania .My to:

  1. Co jest crosses initialization?
  2. Dlaczego pierwszy inicjator x + y przekazuje kompilację, ale nie udało się później?
  3. Jakie są problemy z tak zwanym crosses initialization?

EDIT:
Wiem, że powinienem użyć nawiasów, aby określić zakres r ale chcę wiedzieć, dlaczego, na przykład, dlaczego nie-POD nie może być zdefiniowane w multi-case switch.

Dzięki.

+1

Moje zrozumienie, biorąc pod uwagę poniższe odpowiedzi, dla punktu 3 jest to, że ten błąd jest nadmiernym ograniczeniem C++. Jeśli r nie jest używane po etykiecie, nie ma żadnego wpływu (nawet jeśli przykład tutaj używa r, może być usunięty w przypadku 2, a kompilator dałby ten sam błąd). Lepszym dowodem jest to, że jest dozwolone w C, a nawet w C11. – calandoa

+0

Prawdopodobny duplikat [Błąd: Przejdź do etykiety przypadku] (http://stackoverflow.com/questions/5685471/error-jump-to-case-label) –

Odpowiedz

81

Wersja z int r = x + y; nie zostanie skompilowany albo.

Problem polega na tym, że r może wejść w zakres bez uruchamiania inicjalizatora. Kod skompilowałby się dobrze, gdyby całkowicie usunięto inicjator (tzn. Linia odczytałaby int r;).

Najlepszą rzeczą, którą można zrobić, to ograniczyć zakres zmiennej. W ten sposób zadowolisz zarówno kompilator, jak i czytelnika.

switch(i) 
{ 
case 1: 
    { 
     int r = 1; 
     cout << r; 
    } 
    break; 
case 2: 
    { 
     int r = x - y; 
     cout << r; 
    } 
    break; 
}; 

Standard mówi (6,7/3):

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer (8.5).

+0

Ale moje G ++ nie zezwala na 'int r = x + y'. – Jichao

+9

Cóż, moje g ++ nie działa. Sprawdź ponownie lub uaktualnij kompilator. – avakar

+0

dziękuję, to mi pomogło. Myślę, że kompilator C nie zezwala nawet na deklarację po kodzie. Wygląda na to, że C99 pozwala ... http://stackoverflow.com/questions/7859424/why-was-mixing-declarations-and-code-forblue-up-until-c99 –

31

Należy umieścić zawartość case w nawiasach, aby nadać mu zakres, w ten sposób można zadeklarować zmienne lokalne wewnątrz niego:

switch(i) { 
    case 1: 
     { 
      // int r = x + y; -- OK 
      int r = 1; // Failed to Compile 
      cout << r; 
     } 
     break; 
    case 2: 
     ... 
     break; 
}; 
2

Możliwe jest przeniesienie na blok, a nie w sposób, który omija zgłoszeń z inicjalizacji. Program przeskakujący z punktu, w którym zmienna lokalna z automatycznym czasem przechowywania nie jest w zakresie do punktu, w którym znajduje się w zakresie, jest źle sformułowany, chyba że zmienna ma typ POD i jest zadeklarowana bez inicjatora.

[Example: Code: 

void f() 
{ 
    // ... 
    goto lx; // ill-formed: jump into scope of `a' 
    // ... 
ly: 
    X a = 1; 
    // ... 
lx: 
    goto ly; // ok, jump implies destructor 
// call for `a' followed by construction 
// again immediately following label ly 
} 

--end example] 

Przeniesienie ze stanu instrukcji przełączania na etykietę przypadku jest uważane za skok w tym zakresie.

+1

Witamy w Stack Overflow. Powinieneś podać źródła swoich cytatów, to jest C++ 03: 6.7/3. Zdarza się, że jest to ten sam akapit, który cytowałem w mojej odpowiedzi. – avakar

0

Proponuję promować swoją zmienną r przed stwierdzeniem switch. Jeśli chcesz użyć zmiennej poprzek case bloków (lub taką samą nazwę zmiennej, ale różne zwyczaje), zdefiniować go przed switch:

#include <iostream> 
using namespace std; 

int main() 
{ 
    int x, y, i; 
    cin >> x >> y >> i; 
// Define the variable before the switch. 
    int r; 
    switch(i) { 
     case 1: 
      r = x + y 
      cout << r; 
      break; 
     case 2: 
      r = x - y; 
      cout << r; 
      break; 
    }; 
} 

Jedną z zalet jest to, że kompilator nie musi wykonywać lokalny przydział (aka pchający na stos) w każdym bloku case.

Wadą tego podejścia jest to, że przypadki "upadają" do innych przypadków (tj.bez użycia break), ponieważ zmienna będzie miała poprzednią wartość.

+2

Proponuję zrobić to na odwrót. Kompilator nie musi wykonywać "alokacji lokalnej" w obu przypadkach (aw praktyce tak się nie stanie). – avakar