2013-06-19 5 views
14

Rivedere alcuni 3rd party Codice C mi sono imbattuto in qualcosa di simile a:caso Interruttore strano scoping

switch (state) { 
case 0: 
    if (c=='A') { // open brace 
     // code... 
    break; // brace not closed! 
case 1: 
    // code... 
    break; 
    } // close brace! 
case 2: 
    // code... 
    break; 
} 

che nel codice stavo rivedendo sembrava essere solo un errore di battitura, ma sono rimasto sorpreso che compilato con l'errore fuori.

Perché questo C valido?
Qual è l'effetto sull'esecuzione di questo codice rispetto alla chiusura del tutore nel punto previsto?
C'è qualche caso in cui questo potrebbe essere utile?

Edit: Nell'esempio ho guardato tutte le interruzioni erano presenti (come sopra) - ma la risposta potrebbe includere anche il comportamento Se l'interruzione assente nel caso in cui 0 o 1.

+0

Bene, questo è dovuto all'implementazione dispari 'goto label' di switch-case'. Anche se il tuo caso specifico è probabilmente un po 'strano e difficile da trovare in un caso d'uso (guarda [* Duff's Device *] (http://en.wikipedia.org/wiki/Duff%27s_device), però), il il concetto generale di casi di ricaduta (quando lasci la "pausa") può essere davvero molto utile. –

+2

Nel caso in cui ora stiate cercando una spiegazione su come * il dispositivo Duff * funziona, [qui] (http://stackoverflow.com/questions/514118/how-does-duffs-device-work) alcuni sono. – devnull

+0

In questo caso 'case 1:' viene trattato dal compilatore come un'etichetta separata. La sintassi è perfettamente valida, ma quasi certamente (dal contesto) un errore logico in questa situazione. Provalo inviando 'state == 1' a e vedrai il risultato errato. – Chad

risposta

12

Non solo è valida, struttura simile è stata utilizzato nel codice vero e proprio, per esempio, Duff's Device, che è un loop srotolato per la copia di un buffer:

send(to, from, count) 
register short *to, *from; 
register count; 
{ 
     register 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); 
     } 
} 

da una dichiarazione switch realtà solo calcola un indirizzo e salti ad esso, è facile capire il motivo per cui può sovrapporsi con altri strutture di controllo; le linee all'interno di altre strutture di controllo hanno anche indirizzi che possono essere bersagli da salto!

Nel caso in cui è stato presentato, immaginare se non ci fossero switch o break s nel codice. Quando hai terminato di eseguire la partedi una dichiarazione if, continui a procedere, quindi cadrai nello case 2:. Ora, dato che hai il switch e il break, è importante che cosa possa uscire da break. Secondo la MSDN page, “The C break statement”,

La rottura dichiarazione termina l'esecuzione del più vicino racchiude fare, per, interruttore, o mentre dichiarazione in cui appare. Il controllo passa alla dichiarazione che segue l'istruzione terminata.

Dal momento che il più vicino che racchiude fare, per, interruttore, o mentre affermazione è la tua interruttore (si noti che se non è incluso in tale elenco), quindi se si' ri all'interno del blocco then, si trasferisce all'esterno dell'istruzione switch. Ciò che è un po 'più interessante, però, è che cosa succede se inserisci case 0, ma c == 'A' è falso. Quindi il if trasferisce il controllo appena dopo la parentesi di chiusura del blocco then e si inizia a eseguire il codice in case 2.

+1

Il caso c! = 'A' ha senso nel modo in cui lo spieghi - ma questo è FAR dall'intuitivo da una rapida occhiata al codice! – Ricibob

+0

@Ricibob Ci vuole una certa quantità di visualizzazione C come "assemblaggio di alto livello", lo ammetto. Se hai passato un po 'di tempo a guardare come viene compilato il codice C fino al codice assembly/machine, diventa un po' più semplice. Per esempio, in un'istruzione 'if condition then', c'è solo un salto; se la condizione è _false_, salta subito dopo la parte 'then'. Se finisci nella parte "allora", l'esecuzione continua ti porterà anche lì. Non c'è bisogno di una pausa. In un modulo di iterazione, tuttavia, ad esempio, 'while condition block', il blocco viene compilato per tornare all'inizio per testare il ... –

+0

@Ricibob ... di nuovo condizione, e o andare di nuovo nel blocco o saltare a dopo il blocco. Quindi i costrutti di iterazione richiedono 'break' perché il corpo li riporta all'inizio. 'switch' è un po 'strano, dato che non fa iterazione. Ha solo bisogno di 'break' perché è molto comune che i programmatori vogliano eseguire solo un caso e non cadere nel resto, e senza interruzione dovrebbero scrivere una nuova etichetta subito dopo l'istruzione switch e' goto' it da ogni caso . In ogni caso, probabilmente non è bello fare questo tipo di interleaving del flusso di controllo a meno che tu non abbia ... –

3

In C e C++ è legale saltare in loop e se blocchi fino a quando non si passano sopra dichiarazioni di variabili. È possibile controllare this answer per un esempio utilizzando goto, ma non vedo perché le stesse idee non si applicano ai blocchi switch.

La semantica è diversa rispetto a se lo } era superiore a case 1 come previsto.
Questo codice indica effettivamente se state == 0 e c != 'A' quindi andare a case 2 poiché è lì che si trova la parentesi di chiusura dell'istruzione if. Elabora quindi quel codice e raggiunge l'istruzione break alla fine del codice case 2.

+0

+1 per menzionare il salto di dichiarazioni variabili – legends2k