2011-11-16 11 views
5

Natknąłem się dzisiaj na kod z przełącznikiem case i byłem nieco zaskoczony, widząc, jak to działało. Kod był:Czy przełącznik sprawy działa tak?

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

Ku mojemu zaskoczeniu w scenariuszu, w którym zmienna była c, ścieżka wszedł do „kod tutaj” segmentu. Zgadzam się, że nie ma końca na końcu części przełącznika case, ale wyobrażałem sobie, że zamiast tego przejdzie przez default. Kiedy lądujesz na linii case blah:, czy nie sprawdza, czy twoja obecna wartość pasuje do konkretnego przypadku , a dopiero później pozwala ci wejść w konkretny segment? W przeciwnym razie, o co chodzi w sprawie?

Odpowiedz

13

Nazywa się to przypadku upadku przelotowe i jest pożądane zachowanie. Pozwala na współdzielenie kodu między przypadkami.

Przykładem sposobu korzystania przypadku upadku przelotowe zachowanie:

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

Jeśli wpiszesz przełącznik kiedy blah == a, wtedy wykonać function1(), function2() i function3().

Jeśli nie chcesz, aby takie zachowanie było możliwe, możesz zrezygnować z niego, dołączając oświadczenia break.

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

Sposób oświadczenie przełącznik działa to, że będzie to (bardziej lub mniej) wykonać goto skoczyć do sprawy etykiety i biec od tego punktu. Kiedy wykonanie trafi na break, pozostawia blok przełącznika.

+0

: -Dziękuję za wyszukaną odpowiedź! Dostaję, jak to działa. Po prostu jest to sprzeczne z tym, co zdrowy rozsądek mi dyktuje. Nadal uważam, że zachowanie jest sprzeczne z intuicją. Do tej pory myślałem, że ta "sprawa (blah)" była czymś w rodzaju czeku, aby zapewnić jeśli twoja zmienna rzeczywiście jest (bla) .. najwyraźniej to nie działa jak C! – Manish

+2

@Manish: Switch jest często implementowany za pomocą "[branch table] (http://en.wikipedia.org/wiki/Branch_table)", co może pomóc w zrozumieniu. Zdrowy rozsądek nie istnieje w programowaniu, BTW. Potrzebujesz "rozsądku programisty" :) –

+2

@Manish Obydwa zachowania są pożądane, można albo mieć domyślny fall-through i jawnie 'break' out, albo domyślny break-out i jawnie' continue' do następnego przypadku, jeśli jest to pożądane. Ze zdroworozsądkowego punktu widzenia to drugie może wydawać się bardziej naturalne, ale z myślą o realizacji, domyślne przejście jest bardziej naturalne, więc zostało wybrane. –

10

To jest prawidłowe zachowanie i jest określane jako "przewrócenie". Pozwala to na obsługę wielu przypadków za pomocą tego samego kodu. W zaawansowanych sytuacjach możesz chcieć wykonać kod w jednym przypadku, a następnie przejść do innego przypadku.

wymyślony przykład:

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

Znanym przykładem upadku jest [urządzenie Duffa] (http://en.wikipedia.org/wiki/Duff%27s_device). –

+0

To całkiem fajne użycie, ale wyobrażam sobie, że jest to coś, co zwykle kompilator zrobi dla ciebie, kiedy optymalizujesz prędkość. Nigdy bym tego nie chciał zobaczyć w C, chyba że istnieje NAPRAWDĘ dobry powód i odpowiedni blok komentarza wyjaśniający, dlaczego jest to konieczne i jak go utrzymać. – shenles

2

Na szczęście dla nas, C++ nie zależy od twojej wyobraźni :-)

myśleć o etykietach przełączników jak „goto” etykiet, a switch(blah) prostu „idzie na” odpowiednią etykietę, a następnie kod po prostu płynie stamtąd.

2

W rzeczywistości instrukcja przełączania działa tak, jak zaobserwowałeś. Został zaprojektowany w taki sposób, że można łączyć ze sobą kilka skrzynek, aż do momentu, w którym nastąpi przerwa, i zachowuje się jak sito.

Oto przykład rzeczywistym świecie z jednego z moich projektów:

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; 
} 

Kod dla KE_STRING i KE_VOIDPTR przypadkach jest identyczna z wyjątkiem obliczania wielkości w przypadku łańcucha.

4

To się nazywa upadek.

Dokładnie robi to, co widzisz: kilka spraw wykona ten sam fragment kodu.

Jest także wygodny w sposób dodatkowego przetwarzania dla określonego przypadku, a niektóre wspólne logika:

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

Jest to typowe zastosowanie upadku przelotowe w switch-case. W przypadku uruchomienia i uruchomienia, musimy wykonać deallocateResources i zmienić status na STOPPED, ale STARTING wymaga dodatkowego czyszczenia. W powyższy sposób możesz wyraźnie przedstawić "wspólną logikę" oraz dodatkową logikę w STARTING.

ZATRZYMANE, ZATRZYMANE i domyślne są podobne, wszystkie z nich wchodzą w domyślną logikę (która jest ignorowana).

Nie zawsze jest to dobry sposób kodowania, ale jeśli jest dobrze wykorzystany, może lepiej przedstawić logikę.

Powiązane problemy