Utilisation de continue dans une instruction switch

88

Je veux passer du milieu d'une switchinstruction à l'instruction de boucle dans le code suivant:

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

Est-ce une manière valable d'utiliser continue? Les continuedéclarations sont-elles ignorées par les switchdéclarations? C et C ++ diffèrent-ils sur leur comportement ici?

Matt Joiner
la source
Votre idée est bonne mais la boucle ci-dessus ne s'exécutera jamais do_something().
antik le
5
Même si le contrôle atteint le cas A ou le cas B?
Alexander Poluektov
18
J'allais dire, antik a tort à ce sujet. Dans le cas de A ou B alors do_something () s'exécutera. Par défaut, il sera libéré sous caution.
Antony Woods
3
@acron, c'est le comportement prévu
Matt Joiner
1
Je pense que cela semble beaucoup plus suspect et déroutant et donc plus nocif qu'un goto.
phoeagon

Réponses:

56

C'est bien, l' continueinstruction se rapporte à la boucle englobante, et votre code devrait être équivalent à (en évitant de telles instructions de saut):

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

Mais si vous prévoyez breakde quitter la boucle, comme votre commentaire le suggère (il essaie toujours à nouveau avec un autre élément, jusqu'à ce qu'il soit évalué à faux), vous aurez besoin d'une structure différente.

Par exemple:

do {
    something = get_something();
} while (!(something == A || something == B));
do_something();
visiteur
la source
2
«équivalent» uniquement au sens sémantique. le code généré par le compilateur est très différent. avec une instruction switch, le compilateur peut générer une table de sauts qui évite les comparaisons multiples et est donc beaucoup plus rapide que de comparer avec chaque élément 1 par 1.
chacham15
@ chacham15, pourquoi le compilateur n'a-t-il pas pu générer le même code pour les deux?
avakar
Les instructions switch @avakar génèrent des tables de sauts tandis que l'autre représentation est une série d'évaluations logiques. google jump table pour plus d'informations.
chacham15
@ chacham15, qu'est-ce qui empêche le compilateur de générer une table de saut pour les deux instructions if ou une série d'évaluations logiques pour le commutateur?
avakar le
1
Les instructions de commutateur @avakar exigent que les cas soient des valeurs constantes, car ce n'est pas le cas avec les instructions if, il ne peut pas faire la même optimisation (note: je parle dans le cas général, c'est possible compte tenu de certaines exigences (par exemple, des valeurs const uniquement , seulement certains opérateurs logiques, etc.) pour faire l'optimisation, mais il est fortement dépendant du compilateur et YMMV).
chacham15 le
20

Oui, c'est OK - c'est comme l'utiliser dans une ifdéclaration. Bien sûr, vous ne pouvez pas utiliser a breakpour sortir d'une boucle depuis l'intérieur d'un commutateur.


la source
Mais ifn'a aucun effet sur le comportement de continueou break. Comment voulez-vous dire que c'est pareil?
Matt Joiner
@Matt Je veux dire qu'il continuera la boucle dans les deux cas.
1
@Neil, d'accord, confusion évitée.
Matt Joiner le
1
@ KiJéy Je ne trouve aucune référence à ce sujet, et depuis de nombreuses années de programmation en C, je n'ai jamais vu de compilateur qui le supporte ... Où diable avez-vous trouvé cela?
arjunyg
2
Wow désolé, j'ai vu ça en PHP, j'ai pensé que c'était une vieille pratique, il s'avère que c'est juste PHP ...
Ki Jéy
15

Oui, continue sera ignoré par l'instruction switch et passera à la condition de la boucle à tester. J'aimerais partager cet extrait de la référence The C Programming Language par Ritchie:

La continuedéclaration est liée à break, mais moins souvent utilisée; il provoque la prochaine itération de la enfermant for, whileou doboucle pour commencer. Dans le whileet do, cela signifie que la partie test est exécutée immédiatement; dans le for, le contrôle passe à l'étape d'incrémentation.

L'instruction continue s'applique uniquement aux boucles, pas à une switchinstruction. Un continueinside a switchinside a loop provoque l'itération de boucle suivante.

Je ne suis pas sûr de cela pour C ++.

Islam Elshahat
la source
8

C'est syntaxiquement correct et stylistiquement correct.

Un bon style exige que chaque case:déclaration se termine par l'un des éléments suivants:

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

De plus, suivre case (x):immédiatement avec

 case (y):
 default:

est autorisé - regrouper plusieurs cas qui ont exactement le même effet.

Tout le reste est soupçonné d'être une erreur, tout comme if(a=4){...} Bien sûr , vous devez boucle enfermant ( while, for, do...while) pour continuetravailler. Il ne reviendra pas case()seul. Mais une construction comme:

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

...est correct.

SF.
la source
2
désolé pour le nécropostage, mais j'ajouterais qu'un appel à exitserait également généralement une bonne chose pour terminer un cas de commutation.
Vality
1
Eh bien, je vais avoir tout le Dr Frankenstein ici et souligner également qu'il default:n'est pas nécessaire que ce soit la dernière / dernière entrée - comme le souligne cette question ...
SlySven
1
@SlySven: Lexiquement, ce n'est pas le cas, mais si vous n'avez pas de bonne raison de ne pas le faire durer, faites-le durer. Il est souvent déroutant s'il est utilisé à d'autres endroits.
SF.
1
@SF. eh bien, je peux facilement imaginer les cas où il serait plus logique de rendre le default:cas le premier au lieu du dernier. Comme "faites ceci, sauf si vous obtenez les valeurs inhabituelles suivantes, qui doivent être traitées comme suit".
Ruslan le
5

Bien que techniquement valides, tous ces sauts obscurcissent le flux de contrôle - en particulier la continuedéclaration.

J'utiliserais une telle astuce en dernier recours, pas en premier.

Que diriez-vous

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

Il est plus court et effectue ses tâches de manière plus claire.

Alexander Poluektov
la source
1
désolé pour la confusion Alexander, le code est pour la démonstration seulement, j'ai de bonnes raisons (je crois) pour la structure réelle de mon code.
Matt Joiner
2
@Matt: Cela signifierait probablement une structure encore plus obscure ... :)
visiteur le
-2

Cela peut être un mégabit trop tard, mais vous pouvez utiliser continue 2.

Certains builds / configs PHP afficheront cet avertissement:

Avertissement PHP: le commutateur de ciblage "continuer" équivaut à "break". Vouliez-vous utiliser "continuer 2"?

Par exemple:

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

Cela produira:

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

Testé avec PHP 5.5 et supérieur.

Adrian S
la source
3
Ce n'est PAS une question PHP.
Geoffrey
-4

Switch n'est pas considéré comme une boucle, vous ne pouvez donc pas utiliser Continue dans une instruction case dans switch ...

Jitendra Nagar
la source
3
L' switchinstruction est à l'intérieur d'une whileboucle, elle continueest donc parfaitement valide.
Rick