Perché le variabili non possono essere dichiarate in un'istruzione switch?


945

Mi sono sempre chiesto questo: perché non puoi dichiarare le variabili dopo un'etichetta del caso in un'istruzione switch? In C ++ puoi dichiarare le variabili praticamente ovunque (e dichiararle vicine al primo utilizzo è ovviamente una buona cosa) ma comunque non funzionerà:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

Quanto sopra mi dà il seguente errore (MSC):

l'inizializzazione di "newVal" viene ignorata dall'etichetta "case"

Questa sembra essere una limitazione anche in altre lingue. Perché è un tale problema?


10
Per una spiegazione sulla base della grammatica C BNF, vedere stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/...
Johne

Ecco una buona lettura delle istruzioni e delle etichette degli interruttori (ABC :) in generale.
Etherealone,

4
Direi "Perché non è possibile inizializzare le variabili in un'istruzione switch anziché dichiararle". Dal momento che dichiarare la variabile mi dà solo un avvertimento in MSVC.
ZoomIn

Risposte:


1144

Casele dichiarazioni sono solo etichette . Ciò significa che il compilatore interpreterà questo come un salto direttamente all'etichetta. In C ++, il problema qui è di portata. Le parentesi graffe definiscono l'ambito come tutto all'interno switchdell'istruzione. Ciò significa che ti viene lasciato un ambito in cui un salto verrà eseguito ulteriormente nel codice saltando l'inizializzazione.

Il modo corretto di gestirlo è definire un ambito specifico per casequell'istruzione e definire la variabile al suo interno:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}

94
Relativo all'apertura di un nuovo ambito: favorire la leggibilità e la coerenza nel codice. Ai vecchi tempi, potresti avere automaticamente un frame di stack "extra", ma ora non dovrebbe essere il caso di alcun compilatore di ottimizzazione decente.
Tall Jeff,

10
Sono d'accordo con Jeff: è fin troppo facile "assumere" l'ambito durante la lettura di un'istruzione switch a causa dello stile di rientro utilizzato dalla maggior parte delle persone. Il mio stile è quello di aprire sempre un nuovo ambito per ogni caso / default se è lungo più di una riga.
Offerte del

39
workmad3 - Puoi trovarmi qualche compilatore C ++ che genererà un nuovo frame dello stack se non dichiari nuove variabili? Mi hai preoccupato brevemente, ma nessuno di G ++ 3.1, Visual C ++ 7 o Intel C ++ 8 genererà alcun codice per nuovi ambiti in cui non si dichiarano variabili.
Chris Jefferson,

10
@ workmad3 inserendo un nuovo blocco parentesi graffe non provocare una nuova pila telaio stackoverflow.com/questions/2759371/...
MTVS

3
@TallJef Non so a quali "vecchi tempi" ti riferisci. Non ho mai incontrato un compilatore in cui tutto lo spazio di stack per un metodo non è allocato quando il metodo viene immesso, in 40 anni.
Marchese di Lorne,

333

Questa domanda è stata originariamente contrassegnata come [C] e [C ++] allo stesso tempo. Il codice originale non è effettivamente valido sia in C che in C ++, ma per ragioni completamente diverse e non correlate.

  • In C ++ questo codice non è valido perché l' case ANOTHER_VAL:etichetta passa nell'ambito della variabile newValbypassando la sua inizializzazione. I salti che aggirano l'inizializzazione di oggetti automatici sono illegali in C ++. Questo aspetto del problema viene risolto correttamente dalla maggior parte delle risposte.

  • Tuttavia, nel linguaggio C bypassare l'inizializzazione delle variabili non è un errore. Saltare nell'ambito di una variabile sulla sua inizializzazione è legale in C. Significa semplicemente che la variabile viene lasciata non inizializzata. Il codice originale non viene compilato in C per un motivo completamente diverso. L'etichetta case VAL:nel codice originale è allegata alla dichiarazione di variabile newVal. Nel linguaggio C le dichiarazioni non sono dichiarazioni. Non possono essere etichettati. E questo è ciò che causa l'errore quando questo codice viene interpretato come codice C.

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }

L'aggiunta di un {}blocco aggiuntivo risolve i problemi C ++ e C, anche se questi problemi sembrano essere molto diversi. Sul lato C ++ limita l'ambito di newVal, assicurandosi che case ANOTHER_VAL:non salti più in quell'ambito, il che elimina il problema C ++. Dal lato C questo extra {}introduce un'istruzione composta, rendendo così l' case VAL:etichetta da applicare a un'istruzione, che elimina il problema C.

  • Nel caso C il problema può essere facilmente risolto senza il {}. Basta aggiungere un'istruzione vuota dopo l' case VAL:etichetta e il codice diventerà valido

    switch (val)  
    {  
    case VAL:;            /* Now it works in C! */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:  
      ...
      break;
    }

    Si noti che anche se ora è valido dal punto di vista C, rimane invalido dal punto di vista C ++.

  • Simmetricamente, nel caso C ++ il problema può essere facilmente risolto senza {}. Basta rimuovere l'inizializzatore dalla dichiarazione delle variabili e il codice diventerà valido

    switch (val)  
    {  
    case VAL: 
      int newVal;
      newVal = 42;  
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }

    Si noti che anche se ora è valido dal punto di vista C ++, rimane invalido dal punto di vista C.


4
@AnT: capisco perché quello che corregge C ++ non è applicabile per C; tuttavia, non riesco a capire come risolve il problema C ++ di saltare l'inizializzazione in primo luogo? Non salterebbe comunque la dichiarazione e l'assegnazione di newValquando salta a ANOTHER_VAL?
legends2k

13
@ legends2k: Sì, lo salta ancora. Tuttavia, quando dico "risolve il problema", intendo che corregge l'errore del compilatore C ++ . In C ++ è illegale saltare una dichiarazione scalare con l'inizializzatore , ma è perfettamente corretto saltare una dichiarazione scalare senza inizializzatore . Al case ANOTHER_VAL:punto la variabile newValè visibile, ma con valore indeterminato.
AnT

3
Affascinante. Ho trovato questa domanda dopo aver letto §A9.3: Compound Statementda K&R C (seconda edizione). La voce menzionava la definizione tecnica di una dichiarazione composta che è {declaration-list[opt] statement-list[opt]}. Confuso, poiché avevo pensato che una dichiarazione fosse una dichiarazione, ho cercato e trovato immediatamente questa domanda, un esempio in cui tale disparità diventa evidente e in realtà interrompe un programma. Credo che un'altra soluzione (per C) sarebbe quella di mettere un'altra affermazione (forse un'istruzione nulla?) Prima della dichiarazione in modo che l' istruzione con etichetta sia soddisfatta.
Braden Best

Oops, ho appena notato che la soluzione a dichiarazione nulla che ho suggerito è già nella tua risposta. Non importa, allora.
Braden Best

3
Vale la pena notare che la correzione dell'aggiunta di un'istruzione vuota funziona solo per C99 in poi. In C89, le variabili devono essere dichiarate all'inizio del blocco che li racchiude.
Arthur Tacca,

136

Ok. Solo per chiarire questo strettamente non ha nulla a che fare con la dichiarazione. Si riferisce solo al "salto sull'inizializzazione" (ISO C ++ '03 6.7 / 3)

Molti dei post qui menzionati affermano che saltare la dichiarazione può comportare che la variabile "non venga dichiarata". Questo non è vero. Un oggetto POD può essere dichiarato senza un inizializzatore ma avrà un valore indeterminato. Per esempio:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

Laddove l'oggetto sia un non-POD o aggregato, il compilatore aggiunge implicitamente un inizializzatore, e quindi non è possibile saltare una simile dichiarazione:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

Questa limitazione non è limitata all'istruzione switch. È anche un errore usare 'goto' per saltare su un'inizializzazione:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

Un po 'di curiosità è che questa è una differenza tra C ++ e C. In C, non è un errore saltare l'inizializzazione.

Come altri hanno già detto, la soluzione è aggiungere un blocco nidificato in modo che la durata della variabile sia limitata all'etichetta del singolo caso.


2
"Errore durante l'inizializzazione" ??? Non con il mio GCC. Può dare un avviso "j può essere usato unitializzato" quando si usa j sotto l'etichetta, ma non c'è nessun errore. Tuttavia, in caso di commutazione, si verifica un errore (un errore grave, non un avviso debole).
Mecki,

9
@Mecki: è illegale in C ++. ISO C ++ '03 - 6.7 / 3: "... Un programma che salta da un punto in cui una variabile locale con durata di memorizzazione automatica non rientra nel campo di applicazione in un punto in cui si trova all'interno del campo di applicazione è mal formato a meno che la variabile non abbia il tipo POD (3.9) ed è dichiarato senza inizializzatore (8.5). "
Richard Corden,

1
Sì, ma non è illegale in C (almeno gcc dice che non lo è). j non verrà inizializzato (ha un numero casuale), ma il compilatore lo compila. Tuttavia, nel caso dell'istruzione switch, il compilatore non lo compilerà nemmeno e non vedo la differenza tra un caso goto / label e un caso switch.
Mecki,

8
@Mecki: in generale un singolo comportamento del compilatore non riflette necessariamente ciò che è effettivamente consentito dalla lingua. Ho verificato sia C'90 che C'99 ed entrambi gli standard includono un esempio con un salto sull'inizializzazione in un'istruzione switch.
Richard Corden,

38

L'intera istruzione switch è nello stesso ambito. Per aggirare il problema, procedere come segue:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

Nota le parentesi.


30

Dopo aver letto tutte le risposte e qualche altra ricerca, ottengo alcune cose.

Case statements are only 'labels'

In C, secondo le specifiche,

§6.8.1 Dichiarazioni etichettate:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

In C non esiste alcuna clausola che consenta una "dichiarazione etichettata". Non fa parte della lingua.

Così

case 1: int x=10;
        printf(" x is %d",x);
break;

Questo non verrà compilato , vedere http://codepad.org/YiyLQTYw . GCC sta dando un errore:

label can only be a part of statement and declaration is not a statement

Anche

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

anche questo non è compilando , vedi http://codepad.org/BXnRD3bu . Anche qui sto ottenendo lo stesso errore.


In C ++, secondo le specifiche,

è consentita la dichiarazione con etichetta, ma non è consentita l'inizializzazione con etichetta.

Vedi http://codepad.org/ZmQ0IyDG .


La soluzione a tale condizione è due

  1. Utilizza il nuovo ambito usando {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
  2. O utilizzare la dichiarazione fittizia con etichetta

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. Dichiarare la variabile prima di switch () e inizializzarla con valori diversi nell'istruzione case se soddisfa i requisiti

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }

Altre cose con l'istruzione switch

Non scrivere mai istruzioni nello switch che non fanno parte di alcuna etichetta, perché non verranno mai eseguite:

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

Vedi http://codepad.org/PA1quYX3 .


2
Hai descritto correttamente il problema C. Ma l'affermazione secondo cui in C ++ l'inizializzazione con etichetta non è consentita non è completamente vera. Non c'è niente di sbagliato nell'inizializzazione con etichetta in C ++. Ciò che il C ++ non consente è saltare l' inizializzazione della variabile anell'ambito della variabile a. Quindi, dal punto di vista C, i problemi sono con l' case VAL:etichetta e l'hai descritta correttamente. Ma dal punto di vista del C ++, il problema è con l' case ANOTHER_VAL:etichetta.
AnT

In C ++, a differenza di C, le dichiarazioni sono un sottoinsieme di istruzioni.
Keith Thompson,

20

Non puoi farlo, perché le caseetichette sono in realtà solo punti di ingresso nel blocco contenitore.

Ciò è chiaramente illustrato dal dispositivo di Duff . Ecco un po 'di codice da Wikipedia:

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

Notare come le caseetichette ignorano totalmente i confini del blocco. Sì, questo è male. Questo è il motivo per cui il tuo esempio di codice non funziona. Saltare su caseun'etichetta è lo stesso che usare goto, quindi non puoi saltare una variabile locale con un costruttore.

Come diversi altri manifesti hanno indicato, è necessario inserire un blocco per conto proprio:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }

1
L'implementazione del dispositivo di questo Duff ha un bug che lo rende estremamente lento: count è type int quindi% deve eseguire una vera divisione / modulo. Rendi conte senza segno (o meglio ancora, usa sempre size_t per conteggi / indici) e il problema scompare.
R .. GitHub FERMA AIUTANDO ICE

1
@R ..: Cosa ?! In un sistema a complementi a due, il segno non influenza i moduli per potenze di 2 (è solo un AND sui bit in basso) e non influenza le divisioni per potenze di 2 purché l'architettura del processore abbia un'operazione aritmetica di spostamento a destra ( SARin x86, contro SHRche è per turni senza segno).
Chris Jester-Young,

@ Chris: credo che intenda quando il compilatore deve consentire valori negativi in ​​cui "solo un AND sui bit inferiori" non regge; per esempio, -1% 8 dà -1 sul sistema di complemento di questi due usando g ++ (il segno in questo caso è l'implementazione definita per 5.6 / 4).

3
@ Chris: Sono d'accordo con te sul fatto che R sta esagerando l'impatto; Ho visto solo il tuo commento e sapevo che un semplice AND non era sufficiente.

1
Vale anche la pena notare che il codice Wikipedia originale è per l'invio di dati a un output mappato in memoria, che qui sembra strano perché non è menzionato e ogni byte viene copiato nella stessa posizione "in". Potresti aggirare questo aggiungendo postfix ++ al o menzionando il caso d'uso per l'IO mappato in memoria. Totalmente periferico alla domanda originale :-).
Peter

16

La maggior parte delle risposte finora sono errate sotto un aspetto: puoi dichiarare le variabili dopo l'istruzione case, ma non puoi inizializzarle:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

Come accennato in precedenza, un buon modo per aggirare questo è usare le parentesi graffe per creare un ambito per il tuo caso.


1
Signor 32, hai frainteso qual è il tuo errore: sì, questo non verrà compilato, ma non perché stai dichiarando una variabile all'interno di uno switch. L'errore è perché stai cercando di dichiarare una variabile dopo un'affermazione, che è illegale in C.
MrZebra,

1
Ora un giorno legale in c90 e nella versione più recente di c
Jeegar Patel,

12

Il mio trucco preferito dell'interruttore malefico è usare un if (0) per saltare un'etichetta di caso indesiderata.

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

Ma molto malvagio.


Molto bella. Esempio del perché: il caso 0 e il caso 1 potrebbero ad esempio inizializzare una variabile in modo diverso che viene quindi utilizzata nel caso 2.
hlovdal

1
Se si desidera che sia il caso 0 sia il caso 1 cadano nel caso 2. (senza il caso 0 che cade nel caso 1). Non so se è davvero utile, ma sicuramente funziona.
Petruza,

1
Puoi semplicemente passare all'etichetta richiesta gotosenza offuscare il codice
SomeWittyUsername

10

Prova questo:

switch (val)
{
    case VAL:
    {
        int newVal = 42;
    }
    break;
}

7

È possibile dichiarare le variabili all'interno di un'istruzione switch se si avvia un nuovo blocco:

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

Il motivo è legato all'allocazione (e al recupero) dello spazio nello stack per l'archiviazione delle variabili locali.


1
La variabile può essere dichiarata, ma non può essere inizializzata. Inoltre, sono abbastanza sicuro che il problema non sia in alcun modo correlato allo stack e alle variabili locali.
Richard Corden,

6

Prendere in considerazione:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

In assenza di dichiarazioni di interruzione, a volte newVal viene dichiarato due volte e non si sa se lo fa fino al runtime. La mia ipotesi è che il limite sia dovuto a questo tipo di confusione. Quale sarebbe lo scopo di newVal? La convenzione detterebbe che sarebbe l'intero blocco dell'interruttore (tra le parentesi graffe).

Non sono un programmatore C ++, ma in C:

switch(val) {
    int x;
    case VAL:
        x=1;
}

Funziona bene. Dichiarare una variabile all'interno di un blocco interruttore va bene. Dichiarare dopo una custodia non lo è.


3
@ Mr.32: in realtà il tuo esempio mostra che un printf non viene eseguito, ma in questo caso int x non è un'istruzione ma una dichiarazione, la x viene dichiarata, lo spazio viene riservato ogni volta che l'ambiente di funzione viene impilato, vedi: codepad.org/4E9Zuz1e
Petruza il

Mi aspettavo di trovare questo quando leggevo il titolo della domanda, perché la domanda non riguarda la dichiarazione di variabili all'interno delle etichette "case:", ma nelle istruzioni switch. E solo tu (e VictorH, sottolineando la tua risposta) in realtà hai parlato di variabili nelle istruzioni switch.
termina il

4

L'intera sezione dello switch è un singolo contesto di dichiarazione. Non è possibile dichiarare una variabile in un'istruzione case del genere. Prova questo invece:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}

La variabile può essere dichiarata, ma non può essere inizializzata.
Richard Corden,

@Richard Corden Sono fiducioso che l'inizializzazione funzionerà. Affermate ancora che non può essere inizializzato?
chux - Ripristina Monica il

3

Se il tuo codice dice "int newVal = 42", ti aspetteresti ragionevolmente che newVal non sia mai non inizializzato. Ma se vai su questa affermazione (che è quello che stai facendo), è esattamente quello che succede: newVal è nell'ambito ma non è stato assegnato.

Se questo è ciò che intendevi realmente accadere, allora la lingua richiede di renderlo esplicito dicendo "int newVal; newVal = 42;". Altrimenti puoi limitare l'ambito di newVal al singolo caso, che è più probabile quello che volevi.

Potrebbe chiarire le cose se si considera lo stesso esempio ma con "const int newVal = 42;"


3

Volevo solo enfatizzare il punto di slim . Un costrutto switch crea un ambito cittadino di prima classe. Quindi è possibile dichiarare (e inizializzare) una variabile in un'istruzione switch prima della prima etichetta del caso, senza una coppia di parentesi aggiuntiva:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}

-1 qui int newVal = 42; non verrà mai eseguito. vedi questo codepad.org/PA1quYX3
Jeegar Patel il

4
la dichiarazione int newVal verrà eseguita, ma non l' = 42incarico.
Petruza,

3

Finora le risposte sono state per C ++.

Per C ++, non è possibile saltare un'inizializzazione. È possibile in C. Tuttavia, in C, una dichiarazione non è una dichiarazione e le etichette dei casi devono essere seguite da dichiarazioni.

Quindi, C valido (ma brutto), C ++ non valido

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

Al contrario, in C ++, una dichiarazione è un'istruzione, quindi quanto segue è C ++ valido, C non valido

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}

1
Il secondo esempio NON è C ++ valido (il test con vc2010 e gcc 4.6.1 C ++ non consente di saltare la parte di inizializzazione. Il messaggio di errore gcc è: inizializzazione incrociata di 'int i'
zhaorufei

3

Interessante che questo vada bene:

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

... ma questo non è:

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

Capisco che una correzione sia abbastanza semplice, ma non capisco ancora perché il primo esempio non dia fastidio al compilatore. Come accennato in precedenza (2 anni prima hehe), la dichiarazione non è ciò che causa l'errore, nonostante la logica. L'inizializzazione è il problema. Se la variabile viene inizializzata e dichiarata sulle diverse righe, viene compilata.


1
Il primo non va bene su gcc 4.2: "errore: espressione prevista prima di 'int'". Come dicono Peter e Mr.32, "case 0:; int j; ..." e "case 0:; int j = 7; ..." funzionano entrambi. Il problema in C è solo che "case <etichetta>: dichiarazione" non è una sintassi C valida.
dubiousjim,

3

Ho scritto questa risposta originariamente per questa domanda . Tuttavia quando ho finito ho scoperto che la risposta è stata chiusa. Quindi l'ho pubblicato qui, forse qualcuno a cui piacciono i riferimenti allo standard lo troverà utile.

Codice originale in questione:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

In realtà ci sono 2 domande:

1. Perché posso dichiarare una variabile dopo l' caseetichetta?

È perché nell'etichetta C ++ deve essere nella forma:

N3337 6.1 / 1

etichettato-dichiarazione:

...

  • attributo-specificatore-seqopt case constant-expression :statement

...

E nella C++ dichiarazione dichiarazione è anche considerato come una dichiarazione (al contrario di C):

N3337 6/1:

dichiarazione :

...

Dichiarazione-statement

...

2. Perché posso saltare la dichiarazione delle variabili e quindi usarla?

Perché: N3337 6.7 / 3

È possibile trasferire in un blocco, ma non in modo da bypassare le dichiarazioni con l'inizializzazione . Un programma che salta (Il trasferimento dalla condizione di un'istruzione switch a un'etichetta case è considerato un salto in questo senso.)

da un punto in cui una variabile con durata di memorizzazione automatica non rientra nell'ambito di applicazione fino a un punto in cui si trova all'interno dell'ambito è scarsamente formata a meno che la variabile non abbia un tipo scalare , un tipo di classe con un costruttore predefinito banale e un distruttore banale, una versione qualificata per cv di uno di questi tipi o una matrice di uno dei tipi precedenti ed è dichiarato senza un inizializzatore (8.5).

Poiché kè di tipo scalare e non è inizializzato nel punto della dichiarazione, è possibile saltare la sua dichiarazione. Questo è semanticamente equivalente:

goto label;

int x;

label:
cout << x << endl;

Tuttavia ciò non sarebbe possibile se xfosse stato inizializzato nel punto di dichiarazione:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;

1

Le nuove variabili possono essere decalate solo nell'ambito del blocco. Devi scrivere qualcosa del genere:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

Naturalmente, newVal ha solo uno scopo all'interno delle parentesi graffe ...

Saluti, Ralph


1

Un switchblocco non è uguale a una successione di if/else ifblocchi.Sono sorpreso che nessun'altra risposta lo spieghi chiaramente.

Considera questa switchaffermazione:

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

Potrebbe essere sorprendente, ma il compilatore non lo vedrà come un semplice if/else if. Produrrà il seguente codice:

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

Le caseistruzioni vengono convertite in etichette e quindi chiamate con goto. Le parentesi creano un nuovo ambito ed è facile capire ora perché non è possibile dichiarare due variabili con lo stesso nome all'interno di aswitch blocco.

Può sembrare strano, ma è necessario supportare il fallthrough (ovvero, non utilizzare breakper consentire l'esecuzione dell'esecuzione successiva case).


0

Credo che il problema a portata di mano sia che la dichiarazione è stata saltata, e tu hai provato a usare il var altrove, non sarebbe stato dichiarato.


0

newVal esiste nell'intero ambito dello switch ma viene inizializzato solo se viene colpito l'arto VAL. Se si crea un blocco attorno al codice in VAL, dovrebbe essere OK.


0

Lo standard C ++ ha: È possibile trasferire in un blocco, ma non in un modo che ignori le dichiarazioni con l'inizializzazione. Un programma che salta da un punto in cui una variabile locale con durata di memorizzazione automatica non rientra nel campo di applicazione a un punto in cui si trova all'interno del campo di applicazione è mal formato a meno che la variabile abbia il tipo POD (3.9) e sia dichiarata senza un inizializzatore (8.5).

Il codice per illustrare questa regola:

#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}

Il codice per mostrare l'effetto di inizializzazione:

#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}

0

Sembra che gli oggetti anonimi possano essere dichiarati o creati in un'istruzione case switch per il motivo per cui non possono essere referenziati e come tali non possono passare al caso successivo. Considera questo esempio compilato su GCC 4.5.3 e Visual Studio 2008 (potrebbe essere un problema di conformità anche per cui gli esperti devono valutare)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}

Se lo voterai in basso, spiega perché. Sono curioso di sapere perché la creazione di un oggetto anonimo sembra essere un'esenzione.
Olumide,

1
non un DV, ma: l'intera domanda riguarda la dichiarazione / ambito delle variabili nominate. Un temporaneo ("oggetto anonimo" non è un termine) non è una variabile con nome, né è una dichiarazione, né è soggetto a scopo (a meno che non sia associato a un constriferimento con ambito a sé stante). È un'espressione che vive e muore all'interno della sua affermazione (ovunque essa sia). Pertanto, è del tutto irrilevante.
underscore_d

Foo();non è una dichiarazione; la domanda riguarda le dichiarazioni.
MM,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.