Używanie continue w instrukcji switch

88

Chcę przejść ze środka switchinstrukcji do instrukcji pętli w następującym kodzie:

while (something = get_something())
{
    switch (something)
    {
    case A:
    case B:
        break;
    default:
        // get another something and try again
        continue;
    }
    // do something for a handled something
    do_something();
}

Czy to prawidłowy sposób użycia continue? Czy continuestwierdzenia są ignorowane przez switchoświadczenia? Czy C i C ++ różnią się tutaj zachowaniem?

Matt Joiner
źródło
Twój pomysł jest w porządku, ale powyższa pętla nigdy nie zostanie wykonana do_something().
antik
5
Nawet jeśli kontrola osiągnie przypadek A lub przypadek B?
Alexander Poluektov
18
Chciałem powiedzieć, że antik się myli. W przypadku A lub B zostanie wykonana funkcja do_something (). Domyślnie zostanie zwolniony za kaucją.
Antony Woods
3
@acron, takie jest zamierzone zachowanie
Matt Joiner
1
Myślę, że wygląda to o wiele bardziej podejrzanie i zagmatwanie, a zatem bardziej szkodliwe niż plik goto.
phoeagon

Odpowiedzi:

56

W porządku, continueinstrukcja odnosi się do otaczającej pętli, a Twój kod powinien być równoważny z (unikając takich instrukcji skoku):

while (something = get_something()) {
    if (something == A || something == B)
        do_something();
}

Ale jeśli spodziewasz breaksię wyjścia z pętli, jak sugeruje twój komentarz (zawsze próbuje ponownie z czymś innym, dopóki nie uzyska wartości false), będziesz potrzebować innej struktury.

Na przykład:

do {
    something = get_something();
} while (!(something == A || something == B));
do_something();
gość
źródło
2
„odpowiednik” tylko w sensie semantycznym. kod generowany przez kompilator jest bardzo różny. za pomocą instrukcji switch kompilator może wygenerować tablicę skoków, która pozwala uniknąć wielokrotnych porównań, a tym samym jest znacznie szybsza niż porównywanie z każdym elementem 1 na 1.
chacham15
@ chacham15, dlaczego kompilator nie mógł wygenerować tego samego kodu dla obu?
avakar
Instrukcje przełączające @avakar generują tabele skoków, podczas gdy inna reprezentacja to seria logicznych ocen. tabela skoków google, aby uzyskać więcej informacji.
chacham15
@ chacham15, co uniemożliwia kompilatorowi wygenerowanie tabeli skoków dla dwóch instrukcji if lub serii logicznych obliczeń dla przełącznika?
avakar
1
Instrukcje przełączające @avakar wymagają, aby przypadki były stałymi wartościami, ponieważ nie ma to miejsca w przypadku instrukcji if, nie można dokonać tej samej optymalizacji (uwaga: mówię w ogólnym przypadku, jest to możliwe przy określonych wymaganiach (np. tylko wartości const , tylko niektóre operatory logiczne itp.), aby dokonać optymalizacji, ale jest to wysoce zależne od kompilatora i YMMV).
chacham15
20

Tak, jest OK - to tak, jakby używać go w ifoświadczeniu. Oczywiście nie można użyć breakdo wyrwania pętli z wnętrza przełącznika.


źródło
Ale ifnie ma wpływu na zachowanie continuelub break. Co masz na myśli mówiąc, że jest podobny?
Matt Joiner
@Matt Mam na myśli to, że będzie kontynuował pętlę w obu przypadkach.
1
@Neil, ok, nie udało się uniknąć zamieszania.
Matt Joiner
1
@ KiJéy Nie mogę znaleźć żadnego odniesienia do tego, a przez wiele lat programowania w C nigdy nie widziałem żadnego kompilatora, który to obsługuje ... Gdzie do diabła to znalazłeś?
arjunyg
2
Wow przepraszam, widziałem to w PHP, myślałem, że to stara praktyka, okazuje się, że to tylko PHP ...
Ki Jéy
15

Tak, kontynuacja zostanie zignorowana przez instrukcję switch i przejdzie do stanu testowanej pętli. Chciałbym udostępnić ten fragment z referencji The C Programming Language autorstwa Ritchie:

continueOświadczenie jest związane break, ale rzadziej stosowane; powoduje kolejną iterację zamykającego for, whilelub dopętli, aby rozpocząć. W whilei dooznacza to, że część testowa jest wykonywana natychmiast; w for, kontrola przechodzi do kroku inkrementacji.

Instrukcja continue dotyczy tylko pętli, a nie switchinstrukcji. A continuewewnątrz switchpętli powoduje kolejną iterację pętli.

Nie jestem tego pewien w przypadku C ++.

Islam Elshahat
źródło
8

Jest poprawna składniowo i stylistycznie w porządku.

Dobry styl wymaga, aby każde case:stwierdzenie kończyło się jednym z poniższych:

 break;
 continue;
 return (x);
 exit (x);
 throw (x);
 //fallthrough

Dodatkowo, case (x):natychmiast po

 case (y):
 default:

jest dopuszczalne - łączenie kilku przypadków, które mają dokładnie ten sam skutek.

Coś jeszcze podejrzewa się błąd, podobnie jak if(a=4){...} Oczywiście trzeba zamykając pętlę ( while, for, do...while) do continuedo pracy. Nie zapętli się z powrotem do case()samotności. Ale konstrukcja taka jak:

while(record = getNewRecord())
{
    switch(record.type)
    {
        case RECORD_TYPE_...;
            ...
        break;
        default: //unknown type
            continue; //skip processing this record altogether.
    }
    //...more processing...
}

...jest ok.

SF.
źródło
2
przepraszam za nekroposty, ale dodam, że połączenie z numerem exitrównież byłoby dobrą rzeczą, aby zakończyć sprawę przełącznika.
Vality
1
Cóż, mam zamiar mieć tutaj wszystkich dr Frankensteina , a także wskazać, że default:nie musi to być ostatni / dolny wpis - jak wskazuje to pytanie ...
SlySven
1
@SlySven: Leksykalnie tak nie jest, ale jeśli nie masz dobrego powodu, aby tego nie robić, niech to trwa. Często jest mylące, jeśli jest używane w innych miejscach.
SF.
1
@SF. cóż, łatwo mogę sobie wyobrazić przypadki, w których bardziej sensowne byłoby uczynienie default:przypadku pierwszym, a nie ostatnim. Na przykład „zrób to, chyba że otrzymasz następujące nietypowe wartości, które powinny być obsługiwane w następujący sposób”.
Rusłan
5

Chociaż technicznie poprawne, wszystkie te skoki zaciemniają przepływ sterowania - zwłaszcza continueoświadczenie.

Takiego triku użyłbym w ostateczności, a nie jako pierwszy.

Co powiesz na

while (something = get_something())
{
    switch (something)
    {
    case A:
    case B:
        do_something();
    }        
}

Jest krótszy i działa w bardziej przejrzysty sposób.

Alexander Poluektov
źródło
1
przepraszam za zamieszanie Alexander, kod służy tylko do demonstracji, mam dobry powód (jak sądzę) dla faktycznej struktury w moim kodzie.
Matt Joiner
2
@Matt: To prawdopodobnie oznaczałoby jeszcze bardziej zaciemnioną strukturę ... :)
gość
-2

Może to być megabit za późno, ale możesz użyć continue 2.

Niektóre kompilacje / konfiguracje php wyświetlą to ostrzeżenie:

Ostrzeżenie PHP: przełącznik kierowania „kontynuuj” jest równoważny z „przerwaniem”. Czy chodziło Ci o użycie „kontynuuj 2”?

Na przykład:

$i = 1;

while ($i <= 10) {
    $mod = $i % 4;
    echo "\r\n out $i";
    $i++;
    switch($mod)
    {
        case 0:
            break;
        case 2:
            continue;
            break;
        default:
            continue 2;
            break;
    }
    echo " is even";
}

Spowoduje to wyświetlenie:

out 1
out 2 is even
out 3
out 4 is even
out 5
out 6 is even
out 7
out 8 is even
out 9
out 10 is even

Testowane z PHP 5.5 i nowszym.

Adrian S.
źródło
3
To NIE jest pytanie PHP.
Geoffrey,
-4

Przełącznik nie jest traktowany jako pętla, więc nie można używać Continue w instrukcji case w przełączniku ...

Jitendra Nagar
źródło
3
switchStwierdzenie jest wewnątrz whilepętli, więc continuejest całkowicie poprawny.
Rick