Usando continue em uma instrução switch


88

Quero pular do meio de uma switchinstrução para a instrução de loop no seguinte código:

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

Esta é uma forma válida de usar continue? As continuedeclarações são ignoradas pelas switchdeclarações? C e C ++ diferem em seu comportamento aqui?


Sua ideia é boa, mas o loop acima nunca será executado do_something().
Antik

5
Mesmo se o controle atingir o caso A ou o caso B?
Alexander Poluektov

18
Eu ia dizer, Antik está errado sobre isso. No caso de A ou B, então do_something () será executado. Com o padrão, ele vai cair.
Antony Woods

3
@acron, esse é o comportamento pretendido
Matt Joiner

1
Acho que isso parece muito mais suspeito e confuso e, portanto, mais prejudicial do que um goto.
Phoeagon

Respostas:


56

Tudo bem, a continueinstrução está relacionada ao loop envolvente e seu código deve ser equivalente a (evitando essas instruções de salto):

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

Mas se você espera break sair do loop, como seu comentário sugere (ele sempre tenta novamente com outro algo, até que seja avaliado como falso), você precisará de uma estrutura diferente.

Por exemplo:

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

2
'equivalente' apenas no sentido semântico. o código que o compilador gera é muito diferente. com uma instrução switch, o compilador pode gerar uma tabela de salto que evita múltiplas comparações e, portanto, é muito mais rápido do que comparar cada elemento 1 por 1.
chacham15

@ chacham15, por que o compilador não conseguiu gerar o mesmo código para ambos?
avakar

As instruções de switch @avakar geram tabelas de salto enquanto a outra representação é uma série de avaliações lógicas. tabela de salto do google para mais informações.
chacham15

@ chacham15, o que impede o compilador de gerar uma tabela de salto para as duas instruções if ou uma série de avaliações lógicas para a chave?
avakar

1
As instruções @avakar switch requerem que os casos sejam valores constantes, porque esse não é o caso com as instruções if, não podem fazer a mesma otimização (nota: estou falando no caso geral, é possível dados certos requisitos (por exemplo, apenas valores const , apenas certos operadores lógicos, etc.) para fazer a otimização, mas é altamente dependente do compilador e YMMV).
chacham15

20

Sim, está tudo bem - é como usá-lo em uma ifdeclaração. Claro, você não pode usar um breakpara sair de um loop de dentro de um switch.


Mas ifnão tem efeito sobre o comportamento de continueou break. Como você quer dizer que é igual?
Matt Joiner

@Matt quero dizer que continuará o loop em ambos os casos.

1
@ Neil, ok, confusão evitada.
Matt Joiner

1
@ KiJéy Não consigo encontrar nenhuma referência para isso, e em muitos anos de programação C nunca vi nenhum compilador que suporte isso ... Onde diabos você encontrou isso?
arjunyg

2
Uau, desculpe, eu vi isso em PHP, pensei que era uma prática antiga, mas é só PHP ...
Ki Jéy

15

Sim, continue será ignorado pela instrução switch e irá para a condição do loop a ser testado. Eu gostaria de compartilhar este trecho da referência da Linguagem de Programação C feita por Ritchie:

A continuedeclaração está relacionada a break, mas usada com menos frequência; isso faz com que a próxima iteração do anexando for, whileou doloop para começar. No whilee do, isso significa que a parte de teste é executada imediatamente; no for, o controle passa para a etapa de incremento.

A instrução continue se aplica apenas a loops, não a uma switchinstrução. Um continuedentro de um switchloop causa a próxima iteração do loop.

Não tenho certeza sobre isso para C ++.


8

É sintaticamente correto e estilisticamente correto.

O bom estilo exige que cada case:declaração termine com um dos seguintes:

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

Além disso, seguindo case (x):imediatamente com

 case (y):
 default:

é permitido - agrupar vários casos que têm exatamente o mesmo efeito.

Qualquer outra coisa é suspeito de ser um erro, assim como if(a=4){...} Claro que você precisa encerrando loop ( while, for, do...while) para continueo trabalho. Não vai voltar a case()ficar sozinho. Mas uma construção como:

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

...esta bem.


2
desculpe por necroposting, mas eu acrescentaria que uma chamada para exittambém normalmente seria uma boa coisa para encerrar um caso de troca.
Vality

1
Bem, vou trazer todo o Dr. Frankenstein aqui e também apontar que default:não precisa ser a última entrada / parte inferior - como esta pergunta indica ...
SlySven

1
@SlySven: Lexicamente, não, mas se você não tem um bom motivo para não fazer durar, faça durar. Muitas vezes é confuso se usado em outros lugares.
SF.

1
@SF. bem, posso facilmente imaginar os casos em que faria mais sentido fazer default:o primeiro caso em vez do último. Como "faça isso, a menos que obtenha os seguintes valores incomuns, que devem ser tratados da seguinte maneira".
Ruslan

5

Embora tecnicamente válidos, todos esses saltos obscurecem o fluxo de controle - especialmente a continueinstrução.

Eu usaria esse truque como último recurso, não o primeiro.

E se

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

É mais curto e tem um desempenho mais claro.


1
desculpe a confusão Alexander, o código é apenas para demonstração, tenho um bom motivo (acredito) para a estrutura real do meu código.
Matt Joiner

2
@Matt: Isso provavelmente significaria uma estrutura ainda mais ofuscada ... :)
visitante de

-2

Isso pode demorar um megabit, mas você pode usar continue 2.

Algumas compilações / configurações de php exibirão este aviso:

Aviso do PHP: a opção de direcionamento "continue" é equivalente a "break". Você quis dizer usar "continue 2"?

Por exemplo:

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

Isso resultará em:

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

Testado com PHP 5.5 e superior.


3
Esta NÃO é uma questão de PHP.
Geoffrey

-4

Switch não é considerado um loop, então você não pode usar Continue dentro de uma instrução case em switch ...


3
A switchinstrução está dentro de um whileloop, portanto, continueé perfeitamente válida.
Rick
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.