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?
do...while()
e ele usou esse código como modelo de função desde então.Respostas:
Você pode
break
sairdo{...}while(false)
.fonte
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)
fonte
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.fonte
{}
qualquer lugar .. Isso é especialmente útil noswitch-case
código{...}
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.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.
fonte
Eu vi um código como esse para que você possa usar
break
como umagoto
das sortes.fonte
Esta é apenas uma perversão de
while
obter a semânticagoto tidy-up
sem usar a palavra goto.É uma forma ruim, porque quando você usa outros loops no exterior, torna
while
-breaks
se ambíguo para o leitor. "Isso deveria sair? Ou é apenas para romper o circuito interno?"fonte
Eu acho que é mais conveniente escrever em
break
vez degoto 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:E é dessa maneira que você teria que expressá-lo se quisesse usar
goto
: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
goto
etc.A preocupação mais freqüentemente mencionada sobre o uso de
break
é que ele é muito disfarçadogoto
. Mas, na verdade,break
tem mais semelhanças comreturn
: Ambas as instruções saltam de um bloco de código que é praticamente estruturado em comparação comgoto
. 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.fonte
do{ ... }while(false);
em geral. Mas, na verdade, é sobre usá-lo para emular algum tipo detry{ ... }finally{ ... }
.Esse truque é usado por programadores que são muito tímidos para usar um explícito
goto
em 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ícitogoto
. Em vez disso, eles podem usar umbreak
interior do corpo do ciclo "falso" acima para obter o mesmo efeito.fonte
É 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 umgoto
usado da mesma forma. De fato, isso também reduziria o nível de indentação.Dito isto, porém, notei, muitas vezes esses
do..while
loops tendem a crescer. E então eles obtêmif
s eelse
s dentro, tornando o código realmente não muito legível, muito menos testável.Esses
do..while
sã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 umado..while
das melhores abordagens para fazer uma limpeza.fonte
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 ++.
fonte
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
goto
parecido 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 umif( 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
Isso é acidentalmente chamado como
O "else" será considerado associado ao
gDebugModeOn
, portanto, quandofoo
forfalse
, 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:
Agora, se você usar isso em uma versão compilada dentro de um condicional, ele compila para
Muitos compiladores veem isso como o mesmo que
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:
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:
Ou mais claramente formatado
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.
fonte
x =1; y = 2; { int tmp = y; y = x; x = tmp; }
.#define DBG_PRINT_NUM(n) {}
.if(a) DBG_PRINT_NUM(n); else other();
ele não será compilado. Somenteif(a) {} else other();
ouif(a) ; else other();
é válido, masif(a) {}; else other();
não é, porque isso faz com que a cláusula "if" consista em duas instruções.Talvez seja usado para que
break
possa ser usado dentro para interromper a execução de código adicional a qualquer momento:fonte
goto
apenas porque alguém ouviu que é "ruim".Eu acho que isso é feito para usar
break
oucontinue
declarações. Algum tipo de lógica de código "goto".fonte
É simples: aparentemente você pode sair do loop falso a qualquer momento usando a
break
instrução Além disso, odo
bloco é 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:(Como um aparte: A construção do-while-false é usada em Lua para criar a
continue
declaração que está faltando .)fonte
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
operation
base no endereço apontado pelodata
ponteiro. Como um caso de opção pode ser usado apenas em tipos escalares, primeiro eu o fiz de maneira ineficienteMas 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.Pode-se perguntar por que ele não poderia fazer o mesmo
break
em umaif
declaração, como:break
interage unicamente com o loop ou switch mais próximo , seja umfor
,while
ou umdo .. while
tipo, então infelizmente isso não funcionará.fonte
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.
fonte
Além dos 'exemplos de goto' já mencionados, o idioma do ... while (0) às vezes é usado em uma definição de macro para fornecer colchetes na definição e ainda fazer o compilador trabalhar com a adição de ponto-e-vírgula no final de uma chamada macro.
http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/52f670f1292f30a4?tvc=2&q=time+(0)
fonte
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.
fonte
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
if
não correu bem, pausa.fonte
Muitos já declararam a semelhança entre esse construto e a
goto
e 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?fonte
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 :
Aparelhos vazios (NOP):
por exemplo
Bloco :
que se tornaria
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.
fonte
É uma maneira artificial de emular um,
GOTO
pois esses dois são praticamente idênticos:e:
Por outro lado, esses dois realmente devem ser feitos com
if
s:Embora o aninhamento possa ficar um pouco feio se você apenas transformar uma longa sequência de encaminhamentos
GOTO
em 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.
fonte
do..while(false)
é para rodar o número "indefinido" de vezes, é para rodar uma vez .continue
declaração condicional no final, como mostrei. Porundefined
que 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. Semcontinue
s, eledo..while(false)
será executado uma vez, mas também sembreak
s,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.continue
.continue
NÃO repete o loop. "Acontinue
declaraçã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."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
fonte
Nesses casos, eu uso
fonte
Isso é divertido. Provavelmente há interrupções dentro do loop, como já foi dito. Eu teria feito assim:
fonte
do..while(false)
sempre sai,while(true)
é mais arriscado.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).do{...}while(false)
é exatamente o mesmo quewhile(true){ .. break;}
continue
, eles não são os mesmos.