faça {…} enquanto (falso)


125

Eu estava olhando algum código por um indivíduo e notei que ele parece ter um padrão em suas funções:

<return-type> function(<params>)
{
 <initialization>

 do
 {
   <main code for function>
 }
 while(false);

 <tidy-up & return>
}

Não é ruim , é mais peculiar (o código atual é bastante limpo e sem surpresas). Não é algo que eu já vi antes e me perguntei se alguém pode pensar em alguma lógica por trás disso - talvez em um idioma diferente?


2
Você já tentou alterá-lo para a versão "normal" e ver se ainda compila? Se não conseguir compilar quando gravado normalmente, o erro do compilador pode dar uma pista do motivo pelo qual isso foi feito.
Mark Byers

11
Por que não perguntar ao "indivíduo" em vez de pedir suposições sobre sua intenção?

1
Talvez o professor tenha exigido que ele usasse um do...while()e ele usou esse código como modelo de função desde então.
Hogan

6
@ Neil ... Eu quero evitar parecer idiota quando o confronto e ele explica o uso super avançado de C ++ em que nunca pensei.
Sr. Boy

6
Parece haver algumas respostas diferentes, você pode perguntar ao autor original quais eram suas intenções e postar de volta?
nevets1219

Respostas:


175

Você pode breaksair do{...}while(false).


95
+1 porque essa é provavelmente a intenção do código, mas fazer algo assim é apenas um passo disfarçado de maneira idiota. Se você acha que um goto é a ferramenta certa para o trabalho, então você deve apenas usar um # $ (* # @ Goto.
dsimcha

55
É mais do que um pulo disfarçado. É um goto restrito (estruturado).
Thomas Eding

19
De que maneira é "restrito"? Apenas pular para frente dificilmente é uma "restrição". Um goto é um goto, e vestir um para parecer que não é um é pior do que usar um goto em primeiro lugar.
Anon.

44
@Anon .: Saltar para a frente é uma restrição, e pular é definitivamente uma restrição. O verdadeiro problema com o gotos é o código do espaguete, e um salto para frente e para fora limita muito isso.
David Thornley

33
Um loop real não é semanticamente um goto. Um condicional não é semanticamente um goto. "Vá para o final da função e faça o código de limpeza" é semanticamente um erro. Use gotos quando a semântica se aplicar, não vista algo semanticamente diferente, porque você tem medo deles.
Anon.

126

Muitas pessoas apontam que é frequentemente usado com o break como uma maneira estranha de escrever "goto". Provavelmente isso é verdade se estiver escrito diretamente na função.

Em uma macro, OTOH, do { something; } while (false)é uma maneira conveniente de FORÇAR um ponto-e-vírgula após a invocação de macro, absolutamente nenhum outro token pode seguir.

E outra possibilidade é que já houve um loop lá ou a iteração deverá ser adicionada no futuro (por exemplo, no desenvolvimento orientado a testes, a iteração não era necessária para passar nos testes, mas logicamente faria sentido fazer um loop lá se a função precisava ser um pouco mais geral do que a necessária atualmente)


18
+1 por mencionar a utilidade disso em macros; Estou surpreso que ninguém mais tenha mencionado isso!
Nick Meyer

7
Sim, a coisa da macro é realmente um uso perfeitamente válido disso. Claro, macros de fora, é bobagem ...;)
jalf

12
Não é estranho, é útil, porque é um goto com escopo definido - significa que todas as variáveis ​​que você declara no loop do são destruídas, enquanto o goto não faz isso.
Ana Betts

9
@ Paul: Nada impede que você adicione chaves em torno de um monte de instruções para forçar esse comportamento com goto.
23410 Erikkallen

5
@Paul: sair de um bloco definitivamente faz com que variáveis ​​locais sejam destruídas em C ++, o mesmo que quebra. E em C variáveis ​​não são realmente destruídas, seu espaço de pilha é simplesmente recuperado.
Ben Voigt

25

Provavelmente, a quebra como goto é a resposta, mas apresentarei outra idéia.

Talvez ele quisesse ter variáveis ​​definidas localmente e usou essa construção para obter um novo escopo.

Lembre-se, embora o C ++ recente permita em {...}qualquer lugar, esse nem sempre foi o caso.


30
Ele poderia ter usado aparelho nos dentes nesse caso.
Nemanja Trifunovic

2
@Nemanja, você ficaria surpreso com quantos desenvolvedores não sabem que e tentar algo relacionado ao que Hogan está sugerindo
Polaris878

8
@Polaris @Nemanja, não foi até eu estar programando em C / C ++ há 4 anos que eu descobri que você pode criar um novo escopo local em {} qualquer lugar .. Isso é especialmente útil no switch-casecódigo
Earlz

1
@ Nemanja: Não sei exatamente quando, mas tenho certeza de que em {...}qualquer lugar há um desenvolvimento mais moderno em C ++ (lembre-se de que o primeiro C ++ que usei foi implementado com um pré-processador e que não permitiu esse uso moderno.) o autor era muito antigo.
Hogan

2
Quando não permitia aparelhos em todos os lugares? Comecei a programar há 15 anos, e isso foi permitido na época (tanto no meu livro de texto quanto em todos os compiladores que tentei).
23710 Erikkallen

20

Vi isso como um padrão útil quando existem muitos pontos de saída em potencial para a função, mas o mesmo código de limpeza sempre é necessário, independentemente de como a função é encerrada.

Isso pode tornar a árvore cansativa se / else-if muito mais fácil de ler, basta interromper sempre que um ponto de saída é alcançado, com o restante da lógica em linha posteriormente.

Esse padrão também é útil em idiomas que não possuem uma declaração goto. Talvez seja aí que o programador original aprendeu o padrão.


15
Em seguida, basta usar um goto honesto e direto em vez de um goto pouco disfarçado.
dsimcha

4
Eu gosto desse jeito melhor. É fácil de ler e não carrega o estigma de um goto.
Cameron

8
gotos são perfeitamente fáceis de ler se usados ​​com moderação e localmente. Eles obtiveram seu estigma de volta quando eram a principal forma de controle de fluxo e estavam pulando centenas de linhas.
dsimcha

13
Não usar o goto por causa de um "estigma" é um sinal claro da programação do culto à carga.
Anon.

6
Sem mencionar que uma grande quantidade de código de limpeza é um mau cheiro. Sendo C ++, o código de limpeza normalmente deve estar em destruidores chamados durante o processamento RAII.
David Thornley

12

Eu vi um código como esse para que você possa usar breakcomo uma gotodas sortes.


10

Esta é apenas uma perversão de whileobter a semântica goto tidy-upsem usar a palavra goto.

É uma forma ruim, porque quando você usa outros loops no exterior, torna while- breaksse ambíguo para o leitor. "Isso deveria sair? Ou é apenas para romper o circuito interno?"


10

Eu acho que é mais conveniente escrever em breakvez de goto end. Você nem precisa criar um nome para o rótulo, o que torna a intenção mais clara: você não deseja pular para um rótulo com um nome específico. Você quer sair daqui.

Também é provável que você precise dos aparelhos de qualquer maneira. Então esta é a do{...}while(false);versão:

do {
   // code
   if (condition) break; // or continue
   // more code
} while(false);

E é dessa maneira que você teria que expressá-lo se quisesse usar goto:

{
   // code
   if (condition) goto end;
   // more code
}
end:

Eu acho que o significado da primeira versão é muito mais fácil de entender. Também é mais fácil escrever, mais fácil de estender, mais fácil de traduzir para um idioma que não suporta gotoetc.


A preocupação mais freqüentemente mencionada sobre o uso de breaké que ele é muito disfarçado goto. Mas, na verdade, breaktem mais semelhanças com return: Ambas as instruções saltam de um bloco de código que é praticamente estruturado em comparação com goto. No entanto, ambas as instruções permitem vários pontos de saída em um bloco de código que às vezes pode ser confuso. Afinal, eu tentaria buscar a solução mais clara, qualquer que seja a situação específica.


Ah, acabei de perceber que não entendi completamente a pergunta antes de responder. Eu pensei que era sobre o uso de do{ ... }while(false);em geral. Mas, na verdade, é sobre usá-lo para emular algum tipo de try{ ... }finally{ ... }.
Robert

Boa observação do paralelo entre intervalo e retorno, em vez de ir para o outro lado. Concorde que a solução mais clara é a melhor. Em muitos casos, provavelmente é mais claro encapsular código como esse em sua própria função separada que usa return em vez de break e, em seguida, faz a limpeza na função de chamada.
precisa

7

Esse truque é usado por programadores que são muito tímidos para usar um explícito gotoem seu código. O autor do código acima queria ter a capacidade de pular diretamente para o ponto "limpeza e retorno" no meio do código. Mas eles não queriam usar um rótulo e explícito goto. Em vez disso, eles podem usar um breakinterior do corpo do ciclo "falso" acima para obter o mesmo efeito.


7

É uma prática muito comum. Em c . Eu tento pensar nisso como se você quisesse mentir para si mesmo de uma maneira "Não estou usando um goto". Pensando nisso, não haveria nada de errado com um gotousado da mesma forma. De fato, isso também reduziria o nível de indentação.

Dito isto, porém, notei, muitas vezes esses do..whileloops tendem a crescer. E então eles obtêm ifs e elses dentro, tornando o código realmente não muito legível, muito menos testável.

Esses do..whilesão normalmente destinados a fazer uma limpeza . Por todos os meios possíveis, eu preferiria usar RAII e retornar cedo de uma função curta . Por outro lado, o C não oferece tantas conveniências quanto o C ++ , sendo uma do..whiledas melhores abordagens para fazer uma limpeza.


6

Parece um programador em C. No C ++, variáveis ​​automáticas possuem destruidores que você usa para limpar, portanto não deve haver nada necessário arrumar antes do retorno. Em C, você não tinha esse RAII idioma ; portanto, se você tiver um código de limpeza comum goto, use-o ou use um loop único como acima.

Sua principal desvantagem em comparação com o idioma C ++ é que ele não será organizado se uma exceção for lançada no corpo. C não tinha exceções, portanto, isso não era um problema, mas o torna um mau hábito em C ++.


4

Várias explicações. O primeiro é geral, o segundo é específico para macros do pré-processador C com parâmetros:

Controle de fluxo

Eu já vi isso usado no código C simples. Basicamente, é uma versão mais segura do goto, pois você pode sair dele e toda a memória é limpa corretamente.

Por que algo gotoparecido seria bom? Bem, se você tiver o código, onde praticamente todas as linhas pode retornar um erro, mas você precisa para reagir a todos eles da mesma forma (por exemplo, pela entrega o erro com o seu interlocutor após limpeza), é geralmente mais legível para evitar um if( error ) { /* cleanup and error string generation and return here */ }como evita a duplicação do código de limpeza.

No entanto, em C ++ você tem exceções + RAII exatamente para esse fim, então eu consideraria o estilo de codificação ruim.

Verificação de ponto e vírgula

Se você esquecer o ponto-e-vírgula após uma chamada de macro do tipo função, os argumentos poderão se contrair de maneira indesejada e compilar na sintaxe válida. Imagine a macro

#define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo");

Isso é acidentalmente chamado como

if( foo )
    PRINT_IF_DEBUGMODE_ON("Hullo\n")
else
    doSomethingElse();

O "else" será considerado associado ao gDebugModeOn, portanto, quando foofor false, ocorrerá o reverso exato do que foi planejado.

Fornecendo um escopo para variáveis ​​temporárias.

Como o do / while possui chaves, variáveis ​​temporárias têm um escopo claramente definido, das quais não podem escapar.

Evitando avisos de "ponto e vírgula possivelmente indesejados"

Algumas macros são ativadas apenas nas compilações de depuração. Você os define como:

#if DEBUG
#define DBG_PRINT_NUM(n) printf("%d\n",n);
#else
#define DBG_PRINT_NUM(n) 
#endif

Agora, se você usar isso em uma versão compilada dentro de um condicional, ele compila para

if( foo )
    ;

Muitos compiladores veem isso como o mesmo que

if( foo );

O que geralmente é escrito acidentalmente. Então você recebe um aviso. O do {} while (false) oculta isso do compilador e é aceito por ele como uma indicação de que você realmente não quer fazer nada aqui.

Evitando a captura de linhas por condicionais

Macro do exemplo anterior:

if( foo )
    DBG_PRINT_NUM(42)
doSomething();

Agora, em uma compilação de depuração, como também incluímos habitualmente o ponto-e-vírgula, isso compila perfeitamente. No entanto, na versão compilada, isso de repente se transforma em:

if( foo )

doSomething();

Ou mais claramente formatado

if( foo )
    doSomething();

O que não é exatamente o que foi planejado. Adicionar um {{}} while (false) ao redor da macro transforma o ponto-e-vírgula ausente em um erro de compilação.

O que isso significa para o OP?

Em geral, você deseja usar exceções no C ++ para tratamento de erros e modelos em vez de macros. No entanto, no caso muito raro em que você ainda precise de macros (por exemplo, ao gerar nomes de classe usando colagem de token) ou esteja restrito ao C simples, esse é um padrão útil.


Quanto ao escopo, você não precisa do aparelho de loop; um bloco "sem adornos" é legal: x =1; y = 2; { int tmp = y; y = x; x = tmp; }.
chepner

Um bloco sem adornos não reforça a presença de um ponto e vírgula ausente, o que poderia ter efeitos colaterais indesejados. Fazer enquanto(); EXIGE o ponto-e-vírgula e, portanto, por exemplo, não faz com que as seguintes instruções sejam arrastadas para um "se" se a macro não definir nada em uma versão, como no meu exemplo acima.
uliwitness

Não poderia ser evitado simplesmente fornecendo uma melhor definição para a macro? #define DBG_PRINT_NUM(n) {}.
Chepner # 6/17

1
Não, porque {} é uma declaração completa, ou seja, equivalente a um ";". Então, se você escrever, if(a) DBG_PRINT_NUM(n); else other();ele não será compilado. Somente if(a) {} else other();ou if(a) ; else other();é válido, mas if(a) {}; else other();não é, porque isso faz com que a cláusula "if" consista em duas instruções.
uliwitness

2

Talvez seja usado para que breakpossa ser usado dentro para interromper a execução de código adicional a qualquer momento:

do {
    if (!condition1) break;
    some_code;
    if (!condition2) break;
    some_further_code;
    // …
} while(false);

7
Fazer isso parece uma tentativa de evitar o uso gotoapenas porque alguém ouviu que é "ruim".
Anon.

1
Talvez, mas o C ++ tenha exceções por esse motivo.
TED

2

Eu acho que isso é feito para usar breakou continuedeclarações. Algum tipo de lógica de código "goto".


2

É simples: aparentemente você pode sair do loop falso a qualquer momento usando a breakinstrução Além disso, o dobloco é um escopo separado (que também pode ser alcançado { ... }apenas).

Em tal situação, pode ser uma idéia melhor usar RAII (objetos destruindo automaticamente corretamente quando a função termina). Outra construção semelhante é o uso de goto- sim, eu sei que é mau , mas pode ser usado para ter um código de limpeza comum como este:

<return-type> function(<params>)
{
 <initialization>

 <main code for function using "goto error;" if something goes wrong>

 <tidy-up in success case & return>

 error:

 <commmon tidy-up actions for error case & return error code or throw exception>
}

(Como um aparte: A construção do-while-false é usada em Lua para criar a continuedeclaração que está faltando .)


2

Muitos respondentes deram a razão para do{(...)break;}while(false). Gostaria de complementar a imagem com mais um exemplo da vida real.

No código a seguir, tive que definir o enumerador com operationbase no endereço apontado pelo dataponteiro. Como um caso de opção pode ser usado apenas em tipos escalares, primeiro eu o fiz de maneira ineficiente

if (data == &array[o1])
    operation = O1;
else if (data == &array[o2])
    operation = O2;
else if (data == &array[on])
    operation = ON;

Log("operation:",operation);

Mas como Log () e o restante do código se repetem para qualquer valor escolhido da operação, eu estava pensando em como ignorar o restante das comparações quando o endereço já foi descoberto. E é aqui que do{(...)break;}while(false)vem a calhar.

do {
    if (data == &array[o1]) {
        operation = O1;
        break;
    }
    if (data == &array[o2]) {
        operation = O2;
        break;
    }
    if (data == &array[on]) {
        operation = ON;
        break;
    }
} while (false);

Log("operation:",operation);

Pode-se perguntar por que ele não poderia fazer o mesmo breakem uma ifdeclaração, como:

if (data == &array[o1])
{
    operation = O1;
    break;
}
else if (...)

breakinterage unicamente com o loop ou switch mais próximo , seja um for, whileou um do .. whiletipo, então infelizmente isso não funcionará.


1

Quantos anos tinha o autor?

Eu pergunto porque uma vez me deparei com algum código Fortran em tempo real que fez isso, no final dos anos 80. Acontece que é realmente uma boa maneira de simular threads em um sistema operacional que não os possui. Você apenas coloca o programa inteiro (seu agendador) em um loop e chama suas rotinas de "thread" "uma por uma. As próprias rotinas de thread são loops que iteram até que ocorra uma de várias condições (geralmente uma sendo uma certa quantidade de É "multitarefa cooperativa", já que cabe aos segmentos individuais desistir da CPU de vez em quando para que os outros não fiquem famintos. Você pode aninhar as chamadas de subprograma em loop para simular a prioridade do segmento bandas.



0

Eu concordo com a maioria dos pôsteres sobre o uso como um goto pouco disfarçado. As macros também foram mencionadas como uma motivação potencial para escrever código no estilo.

Também vi esse construto usado em ambientes mistos de C / C ++ como uma exceção do pobre homem. O "do {} while (false)" com uma "quebra" pode ser usado para pular para o final do bloco de código, caso algo que normalmente justifique uma exceção seja encontrado no loop.

Também reparei esse construto usado em lojas onde a ideologia do "retorno único por função" é imposta. Novamente, isso substitui um "ir" explícito - mas a motivação é evitar vários pontos de retorno, não "pular" o código e continuar a execução real nessa função.


0

Trabalho com o Adobe InDesign SDK, e os exemplos do InDesign SDK têm quase todas as funções escritas dessa maneira. É devido ao fato de que a função geralmente é realmente longa. Onde você precisa fazer QueryInterface (...) para obter qualquer coisa do modelo de objeto do aplicativo. Normalmente, todo QueryInterface é seguido por ifnão correu bem, pausa.


0

Muitos já declararam a semelhança entre esse construto e a gotoe expressaram uma preferência pelo goto. Talvez o histórico dessa pessoa incluísse um ambiente em que o Goto fosse estritamente proibido pelas diretrizes de codificação?


0

A outra razão pela qual consigo pensar é que decora os aparelhos, enquanto eu acredito que os aparelhos nus padrão C ++ mais recentes não são bons (a ISO C não gosta deles). Caso contrário, para silenciar um analisador estático como fiapos.

Não sei por que você os deseja, talvez escopo variável ou vantagem com um depurador.

Consulte Trivial Do While loop e Chaves são boas de C2.

Para esclarecer minha terminologia (que acredito seguir o uso padrão):

Aparelhos nus :

init();
...
{
c = NULL;
mkwidget(&c);
finishwidget(&c);
}
shutdown();

Aparelhos vazios (NOP):

{}

por exemplo

while (1)
   {}  /* Do nothing, endless loop */

Bloco :

if (finished)
{
     closewindows(&windows);
     freememory(&cache);
}

que se tornaria

if (finished)
     closewindows(&windows);
freememory(&cache);

se as chaves forem removidas, alterando o fluxo de execução, não apenas o escopo das variáveis ​​locais. Portanto, não 'autônomo' ou 'nu'.

Aparelhos nus ou um bloco podem ser usados ​​para indicar qualquer seção do código que possa ser um potencial para uma função (em linha) que você deseja marcar, mas não refatorar naquele momento.


Realmente? Isso é uma vergonha. Uma das poucas coisas de que eu gostei no C foi permitir que você declarasse um novo escopo aninhado em qualquer lugar.
TED

1
Aparelhos nus são ocasionalmente úteis para corrigir falhas estranhas. Consulte blogs.msdn.com/oldnewthing/archive/2004/05/20/135841.aspx .
22710 Brian Brian

1
Em C ++, um bloco (ou seja, seus "chavetas") pode ser usado em qualquer lugar em que uma única instrução seja permitida.
Ben Voigt

@BenVoigt Aparelhos vazios, isto é, um NOP é diferente de um "aparelho fixo", que é um bloco adicionado em torno de uma sequência linear de instruções. Por exemplo, `printf (" Olá "); {putchar (','); putchar (0x20); } printf ("world! \ n"); `onde os chavetas não fazem parte do controle de loop ou ramificação.
Mctylr

@mctylr: Eu não estava falando sobre suspensórios.
Ben Voigt

0

É uma maneira artificial de emular um, GOTOpois esses dois são praticamente idênticos:

// NOTE: This is discouraged!
do {
    if (someCondition) break;
    // some code be here
} while (false);
// more code be here

e:

// NOTE: This is discouraged, too!
if (someCondition) goto marker;
// some code be here
marker:
// more code be here

Por outro lado, esses dois realmente devem ser feitos com ifs:

if (!someCondition) {
    // some code be here
}
// more code be here

Embora o aninhamento possa ficar um pouco feio se você apenas transformar uma longa sequência de encaminhamentos GOTOem aninhadosif s . A resposta real é refatoração adequada, no entanto, não imitando construções arcaicas de linguagem.

Se você estava tentando desesperadamente transliterar um algoritmo com GOTO s, provavelmente poderia fazê-lo com esse idioma. Certamente não é padrão e é um bom indicador de que você não está aderindo de perto às expressões esperadas do idioma.

Não estou ciente de nenhuma linguagem C, onde do / while é uma solução idiomática para qualquer coisa, na verdade.

Você provavelmente poderia refatorar toda a bagunça em algo mais sensato para torná-la mais idiomática e muito mais legível.


1
Alan, nãodo..while(false) é para rodar o número "indefinido" de vezes, é para rodar uma vez .
Dmitry

2
Ele está sendo executado um número indefinido de vezes se você tiver uma continuedeclaração condicional no final, como mostrei. Por undefinedque eu simplesmente significar que você não sabe se ele está sendo executado mais de uma vez a menos que você pode prever se as condições serão atendidas em uma iteração específica. Sem continues, ele do..while(false)será executado uma vez, mas também sem breaks, while(true)será executado para sempre; portanto, o "comportamento padrão" não é realmente relevante para entender o que pode ser feito com os loops.
Alan Plum

É útil quando você define uma macro que possui várias instruções. Se você os envolver em um do / while (false), poderá usar a macro em uma instrução if / else como qualquer outra chamada de função. veja noveltheory.com/TechPapers/while.htm para uma explicação
John Paquin

5
Alguém não entende a semântica de continue. continueNÃO repete o loop. "A continuedeclaração deve ocorrer apenas em uma declaração de iteração e faz com que o controle passe para a parte de continuação do loop da menor declaração de iteração anexa , ou seja, até o final do loop."
Ben Voigt

-1, as duas principais instruções não são idênticas pelas razões apontadas pelo @BenVoigt.
Cmh 14/08

0

Alguns codificadores preferem ter apenas uma saída / retorno de suas funções. O uso de um manequim faz {....} while (false); permite que você "saia" do loop fictício assim que terminar e ainda tiver um único retorno.

Eu sou um codificador java, então meu exemplo seria algo como

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class p45
{
    static List<String> cakeNames = Arrays.asList("schwarzwald torte", "princess", "icecream");
    static Set<Integer> forbidden = Stream.of(0, 2).collect(Collectors.toSet());

    public static  void main(String[] argv)
    {
        for (int i = 0; i < 4; i++)
        {
            System.out.println(String.format("cake(%d)=\"%s\"", i, describeCake(i)));
        }
    }


    static String describeCake(int typeOfCake)
    {
        String result = "unknown";
        do {
            // ensure type of cake is valid
            if (typeOfCake < 0 || typeOfCake >= cakeNames.size()) break;

            if (forbidden.contains(typeOfCake)) {
                result = "not for you!!";
                break;
            }

            result = cakeNames.get(typeOfCake);
        } while (false);
        return result;
    }
}


-1

Isso é divertido. Provavelmente há interrupções dentro do loop, como já foi dito. Eu teria feito assim:

while(true)
{
   <main code for function>
   break; // at the end.
}

2
Com um potencial de loop para sempre? do..while(false)sempre sai, while(true)é mais arriscado.
Dmitry

1
while(true)é o idioma correto na maioria dos idiomas. Você o encontrará frequentemente nos aplicativos da GUI como o loop principal do programa. Como você supõe basicamente que o programa não deve morrer até que seja solicitado, do..while(false)isso causaria todo tipo de lógica artificial. Essa abordagem pode ser mais arriscada em relação a um ponto de vista perfeccionista, mas é semanticamente mais fácil e, portanto, menos propensa a erros para programadores humanos (desculpe, Skynet).
Alan Plum

2
@dmitry do{...}while(false)é exatamente o mesmo quewhile(true){ .. break;}
N 1.1

4
@ N1.1: Não na presença de continue, eles não são os mesmos.
Ben Voigt
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.