2011-11-16 15 views
5

Oggi mi sono imbattuto in un pezzo di codice del caso-switch e sono rimasto un po 'sorpreso nel vedere come funzionava. Il codice era:Il case-switch funziona così?

switch (blah) 
{ 
case a: 
    break; 
case b: 
    break; 
case c: 
case d: 
case e: 
    { 
    /* code here */ 
    } 
    break; 
default : 
    return; 
} 

Con mia sorpresa nello scenario in cui la variabile è stata c, il percorso è andato all'interno del segmento "qui il codice". Sono d'accordo che non vi è alcuna interruzione alla fine della parte c dell'interruttore del caso, ma avrei immaginato che passasse invece da default. Quando atterri su una linea case blah:, non controlla se il tuo valore attuale corrisponde al caso specifico e solo allora ti lascia nel segmento specifico? Altrimenti qual è il punto di avere un caso?

risposta

13

Questo è chiamato caso caduta-through, ed è un comportamento desiderabile. Ti permette di condividere il codice tra i casi.

Un esempio di come utilizzare caso autunno-through comportamento:

switch(blah) 
{ 
case a: 
    function1(); 
case b: 
    function2(); 
case c: 
    function3(); 
    break; 
default: 
    break; 
} 

Se si inserisce l'interruttore quando blah == a, allora si eseguirà function1(), function2() e function3().

Se non si desidera avere questo comportamento, è possibile disattivarlo includendo le istruzioni break.

switch(blah) 
{ 
case a: 
    function1(); 
    break; 
case b: 
    function2(); 
    break; 
case c: 
    function3(); 
    break; 
default: 
    break; 
} 

Il modo un'istruzione switch funziona è che sarà (più o meno) eseguire un goto di saltare all'etichetta caso, e continuare a correre da quel punto. Quando l'esecuzione raggiunge lo break, lascia il blocco di commutazione.

+0

: -Grazie per l'elaborata risposta! Ho capito come funziona. E 'solo che è contro ciò che il senso comune mi dice. Continuo a pensare che il comportamento sia contro-intuitivo. Ho fino ad oggi pensato che quei "casi (blah)" erano un po' come un assegno per assicurare se la tua variabile è davvero (blah) .. evidentemente non è così che funziona C! – Manish

+2

@Manish: Switch viene spesso implementato con una "[tabella delle diramazioni] (http://en.wikipedia.org/wiki/Branch_table)", in modo che possa aiutare anche la tua comprensione. Il buon senso non esiste nella programmazione, BTW. Hai bisogno invece di "sensei del programmatore" :) –

+2

@Manish Entrambi i comportamenti sono desiderabili, uno può avere default fall-through ed esplicitamente 'break' out, o breakout predefinito ed esplicitamente' continue' al caso successivo se lo si desidera. Da un punto di vista del buon senso, quest'ultimo può sembrare più naturale, ma con l'implementazione in mente, il fall-default è più naturale, quindi è stato scelto. –

10

Questo è il comportamento corretto ed è denominato "caduta attraverso". Ciò consente di avere più casi gestiti dallo stesso codice. In situazioni avanzate, potresti voler eseguire del codice in un caso, quindi passare a un altro caso.

esempio forzato:

switch(command) 
{ 
    case CMD_SAVEAS: 
    { 
     this->PromptForFilename(); 
    } // DO NOT BREAK, we still want to save 
    case CMD_SAVE: 
    { 
     this->Save(); 
    } break; 


    case CMD_CLOSE: 
    { 
     this->Close(); 
    } break; 

    default: 
     break; 
} 
+1

Il famoso esempio di caduta è [dispositivo Duff] (http://en.wikipedia.org/wiki/Duff%27s_device). –

+0

Si tratta di un utilizzo piuttosto interessante, ma immagino che è qualcosa che il compilatore di solito fa per te quando ottimizzi la velocità. Non vorrei mai vederlo scritto in C a meno che non ci fosse una ragione VERAMENTE buona e un blocco di commento appropriato che spieghi perché è necessario e come mantenerlo. – shenles

2

Fortunatamente per noi, C++ non dipende dalla vostra immaginazione :-)

Pensate delle etichette degli interruttori come etichette "Vai a", e il switch(blah) semplicemente "va a" l'etichetta corrispondente, e quindi il codice scorre da lì.

2

In realtà la dichiarazione dell'interruttore funziona come osservato. È progettato in modo da poter combinare più casi insieme fino a quando si verifica un'interruzione e agisce come un setaccio.

Ecco un esempio reale da uno dei miei progetti:

struct keystore_entry *new_keystore(p_rsd_t rsd, enum keystore_entry_type type, const void *value, size_t size) { 
     struct keystore_entry *e; 
     e = rsd_malloc(rsd, sizeof(struct keystore_entry)); 
     if (!e) 
       return NULL; 
     e->type = type; 
     switch (e->type) { 
     case KE_DOUBLE: 
       memcpy(&e->dblval, value, sizeof(double)); 
       break; 
     case KE_INTEGER: 
       memcpy(&e->intval, value, sizeof(int)); 
       break; 

     /* NOTICE HERE */ 

     case KE_STRING: 
       if (size == 0) { 
         /* calculate the size if it's zero */ 
         size = strlen((const char *)value); 
       } 
     case KE_VOIDPTR: 
       e->ptr = rsd_malloc(rsd, size); 
       e->size = size; 
       memcpy(e->ptr, value, size); 
       break; 

     /* TO HERE */ 
     default: 
       return NULL; 
     } 
     return e; 
} 

Il codice per KE_STRING e KE_VOIDPTR casi è identico tranne che per il calcolo delle dimensioni in caso di stringa.

4

Questo è chiamato un fall-through.

Fa esattamente ciò che si sta vedendo: diversi casi eseguiranno lo stesso codice.

E 'anche conveniente nel fare elaborazione aggiuntiva per alcuni casi, e una logica comune:

// psuedo code: 
void stopServer() { 
    switch (serverStatus) 
    case STARTING: 
    { 
     extraCleanUpForStartingServer(); 
     // fall-thru 
    } 
    case STARTED: 
    { 
     deallocateResources(); 
     serverStatus = STOPPED; 
     break; 
    } 
    case STOPPING: 
    case STOPPED: 
    default: 
     // ignored 
     break; 
} 

Questo è un uso tipico della caduta-through in switch-case. In caso di STARTING e STARTED, dobbiamo fare deallocateResources e cambiare lo stato in STOPPED, ma STARTING ha bisogno di una pulizia extra. In questo modo, è possibile presentare chiaramente la "logica comune" più logica aggiuntiva in STARTING.

FERMO, ARRESTO e predefinito sono simili, tutti rientrano attraverso alla logica di default (che è ignorando).

Non è sempre un buon modo per codificare come questo, ma se ben usato può presentare la logica meglio.

Problemi correlati