goto
é quase universalmente desencorajado. Vale a pena usar esta declaração?
Comentários
Resposta
Isso foi discutido várias vezes no Stack Overflow, e Chris Gillum resumiu os possíveis usos de goto
:
Saindo de uma função de forma limpa
Freqüentemente, em uma função, você pode alocar recursos e precisar sair em vários lugares. Os programadores podem simplificar seu código colocando o código de limpeza de recursos no final da função todos os ” pontos de saída ” da função iriam para a etiqueta de limpeza. Dessa forma, você não precisa escrever código de limpeza em cada ” ponto de saída ” da função.
Saindo de loops aninhados
Se você “estiver em um loop aninhado e precisar sair de todos loops, um goto pode tornar isso muito mais limpo e mais simples do que instruções de interrupção e if-checks.
Melhorias de desempenho de baixo nível
Isso só é válido em código crítico para o desempenho, mas as instruções goto são executadas muito rapidamente e podem dar um impulso ao mover-se por uma função. Esta é uma faca de dois gumes, no entanto, porque um compilador normalmente não pode otimizar o código que contém gotos.
Eu diria, como muitos outros diriam , que em todos esses casos, o uso de goto
é usado como um meio de sair de um canto em que alguém se codificou e é geralmente um sintoma de código que pode ser refatorado .
Comentários
- O primeiro problema é resolvido muito bem por
finally
blocos em linguagens modernas, e o segundo é resolvido com os rótulosbreak
s. Se você ‘ ficar preso ao C,goto
é praticamente a única maneira de resolver esses problemas com elegância. - Pontos muito bons. Gosto de como o Java permite quebrar vários níveis de loops
- @Chinmay – blocos finalmente aplicam-se a 1) linguagens modernas que os possuem eb) você pode tolerar a sobrecarga (o tratamento de exceções tem uma sobrecarga. ) Ou seja, usar
finally
só é válido nessas condições. Existem situações muito raras, mas válidas, em que ir para um é o caminho a percorrer. - Ok, pessoal … que diabos é a diferença entre
break 3
oubreak myLabel
egoto myLabel
. Oh, isso ‘ acertou na palavra-chave. Se você estiver usandobreak
,continue
ou alguma palavra-chave semelhante, você usará de fatogoto
(Se você estiver usandofor, while, foreach, do/loop
, você está usando um goto condicional.) - Sobre desempenho de baixo nível – o comportamento do processador moderno pode ser difícil de prever (pipeline, execução fora de ordem), então provavelmente em 99% dos casos não vale a pena ou mesmo pode ser mais lento. @MatthewWhited: Escopo. Se você inserir o escopo em um ponto arbitrário, não ficará claro (para os humanos) como os construtores devem ser chamados (o problema também existe em C).
Resposta
Construções de fluxo de controle de nível superior tendem a corresponder a conceitos no domínio do problema. Um if / else é uma decisão baseada em alguma condição. Um loop diz para executar alguma ação repetidamente. Até mesmo uma instrução break diz “estávamos fazendo isso repetidamente, mas agora precisamos parar”.
Uma instrução goto, por outro lado, tende a corresponder a um conceito no programa em execução, não no domínio do problema. Diz para continuar a execução em um ponto especificado no programa . Alguém lendo o código tem que inferir o que isso significa com respeito ao domínio do problema.
É claro que todas as construções de nível superior podem ser definidas em termos de gotos e ramificações condicionais simples . Isso não significa que eles sejam meramente fantasmas disfarçados. Pense neles como restritos gotos – e são as restrições que os tornam úteis. Uma instrução break é implementada como um salto para o final do loop envolvente, mas é melhor pensá-lo como operando no loop como um todo.
Se todo o resto for igual, o código cuja estrutura reflete a do domínio do problema tende a ser mais fácil de ler e manter.
Não há casos em que uma instrução goto é absolutamente necessária (há “s um teorema para esse efeito), mas há casos em que pode ser a solução menos ruim. Esses casos variam de linguagem para linguagem, dependendo de quais construções de nível superior a linguagem suporta.
Em C, por exemplo, acredito que haja três cenários básicos onde um goto é apropriado.
- Saindo de um loop aninhado. Isso seria desnecessário se a linguagem tivesse uma instrução break rotulada.
- Saindo de um trecho de código (normalmente um corpo de função) em caso de um erro ou outro evento inesperado . Isso seria desnecessário se a linguagem tivesse exceções.
- Implementando uma máquina de estado finito explícita. Neste caso (e, eu acho, apenas neste caso) um goto corresponde diretamente a um conceito no domínio do problema, fazendo a transição de um estado para um outro estado especificado, onde o estado atual é representado por qual bloco de código está sendo executado no momento .
Por outro lado, uma máquina de estados finitos explícita também pode ser implementada com uma instrução switch dentro de um loop. Isso tem a vantagem de que cada estado começa no mesmo lugar no código, o que pode ser útil para depuração, por exemplo.
O principal uso de um goto em uma linguagem razoavelmente moderna (uma que suporta if / senão e loops) é para simular uma construção de fluxo de controle que “está faltando na linguagem.
Comentários
- Esta é realmente a melhor resposta até agora – bastante surpreendente, para uma pergunta que tem um ano e meio. 🙂
- +1 para ” melhor resposta até agora “. — ” O principal uso de um goto em uma linguagem razoavelmente moderna (que suporta if / else e loops) é simular um fluxo de controle construa que ‘ estão faltando no idioma. ”
- Na máquina de estado de instrução switch, cada gravação no valor de controle é realmente um
goto
. SSSM ‘ s são frequentemente boas estruturas para usar, uma vez que vêem separar o estado SM da construção de execução, mas eles não ‘ t realmente eliminam ” gotos “. - ” Sendo todo o resto igual, o código cuja estrutura reflete a do domínio do problema tende a ser mais fácil de ler e manter. ” Eu ‘ sabia disso há muito tempo, mas não tinha palavras para comunicá-lo com tanta precisão de uma maneira que outro os programadores podem e realmente entenderão. Obrigado por isso.
- O ” teorema do programa estruturado ” me diverte, pois tende a demonstrar claramente o aponte que usar instruções de programação estruturada para emular um goto é muito menos legível do que apenas usar um goto!
Resposta
Certamente depende da linguagem de programação. O principal motivo de goto
ser controverso é por causa de seus efeitos nocivos que surgem quando o compilador permite que você o use com muita liberdade. Podem surgir problemas, por exemplo, se permitir que você use goto
de forma que agora você possa acessar uma variável não inicializada, ou pior, pular para outro método e bagunçar a chamada pilha. Deve ser responsabilidade do compilador não permitir o fluxo de controle sem sentido.
Java tentou “resolver” esse problema proibindo goto
inteiramente. No entanto, Java permite que você use return
dentro de um bloco finally
e, portanto, faça com que uma exceção seja inadvertidamente engolida.O mesmo problema ainda está lá: o compilador não está fazendo seu trabalho. Remover goto
da linguagem não corrigiu isso.
Em C #, goto
é tão seguro quanto break
, continue
, try/catch/finally
e return
. Ele não permite que você use variáveis não inicializadas, não permite que você salte de um bloco finally, etc. O compilador reclamará. Isso é porque ele resolve o problema real , que é como eu disse fluxo de controle sem sentido. goto
não cancela magicamente a análise de atribuição definida e outras verificações razoáveis do compilador.
Comentários
- O que você quis dizer é que C # ‘ s
goto
não é mau? Nesse caso, esse é o caso de toda linguagem moderna usada rotineiramente com uma construçãogoto
, não apenas C # …
Resposta
Sim. Quando seus loops estão aninhados em vários níveis de profundidade, goto
é a única maneira de sair elegantemente de um loop interno. A outra opção é definir um sinalizador e interromper cada loop se esse sinalizador satisfizer uma condição. Isso é realmente feio e muito sujeito a erros. Nesses casos, goto
é simplesmente melhor.
Claro, a instrução Java “com o rótulo break
faz o mesmo coisa, mas sem permitir que você pule para um ponto arbitrário no código, o que resolve o problema perfeitamente sem permitir as coisas que tornam goto
mal.
Comentários
- goto nunca é uma resposta elegante. Quando seus loops estão aninhados em vários níveis de profundidade, o problema é que você falhou ao arquitetar seu código para evitar os loops multinestados. Chamadas de procedimento NÃO são o inimigo. (Leia os documentos ” Lambda: The Ultimate … “.) Usando um goto para quebrar loops aninhados vários níveis de profundidade é colocar um band-aid em uma fratura múltipla aberta: pode ser simples, mas não é ‘ a resposta certa.
- E, claro, aí nunca é o caso estranho quando loops profundamente aninhados são chamados? Ser um bom programador isn Não apenas sobre seguir as regras, mas também sobre saber quando quebrá-las.
- @ JohnR.Strohm Nunca diga nunca. Se você usar termos como ” nunca ” na programação, você ‘ claramente não negociou com casos de canto, otimização, restrições de recursos, etc. Em loops profundamente aninhados, goto é, de fato, a maneira mais limpa e elegante de sair dos loops. Não ‘ importa o quanto você ‘ refatorou seu código.
- @ JohnR.Strohm: Segundo mais perdido recurso na mudança de VB.NET para C #: o loop
Do
. Porque? Porque eu posso mudar o único loop que precisa de uma pausa profunda em umDo
loop e digitarExit Do
e lá ‘ s nãoGoTo
. Os loops aninhados acontecem o tempo todo. Quebrar as pilhas acontece alguma% do tempo. - @ JohnR.Strohm A única razão pela qual você diz ” goto nunca é uma resposta elegante ” é porque você decidiu que goto nunca é uma resposta elegante e, portanto, qualquer solução usando goto não pode ser uma resposta elegante.
Resposta
A maior parte do desânimo vem de uma espécie de “religião” que foi criada em torno do Deus Djikstra que era convincente no início dos “anos 60 sobre isso” – seu poder indiscriminado de :
- pule em qualquer lugar em qualquer bloco de código
- função não executada desde o início
- loops não executados desde o início
- inicialização de variável ignorada
- pule de qualquer bloco de código sem qualquer limpeza possível.
Isso não tem mais nada a ver com o goto
declaração das línguas modernas, cuja existência se deve apenas ao suporte da cr eção de estruturas de código diferentes das fornecidas pela linguagem.
Em particular, o primeiro ponto principal acima é mais permitido e o segundo é limpo (se você goto
fora de um bloco, a pilha é desenrolada corretamente e todos os destruidores apropriados são chamados)
Você pode consultar esta resposta para ter uma idéia de como o código não usar goto pode ser ilegível. O problema não é o goto em si, mas o mau uso dele.
Posso escrever um programa inteiro sem usar if
, apenas for
. Claro, não será bem legível, uma aparência desajeitada e desnecessariamente complicada.
Mas o problema não é for
. Sou eu.
Coisas como break
, continue
, throw
, bool needed=true; while(needed) {...}
, etc. estão notando mais do que mascarada goto
para escapar das cimitarras dos fanáticos Djikstrarian, que – 50 anos após a invenção das línguas modernas – ainda querem seus prisioneiros. Eles se esqueceram do que Djikstra estava falando, eles só se lembram do título de sua nota (GOTO considerou prejudicial, e não era nem mesmo seu título único: foi mudado pelo editor) e culpe e bata, bata e culpe todo construto que tenha esses 4 letra colocada em sequência.
É 2011: é hora de entender que goto
não precisa lidar com GOTO
declaração sobre a qual Djikstra era convincente.
Comentários
- ” Coisas como quebrar, continuar , lance, bool necessário = verdadeiro; enquanto (necessário) {…}, etc. estão notando mais do que mascarar goto ” Eu não ‘ não concordo com isso. Eu preferiria que ” coisas como break etc sejam restritas goto ” ou algo parecido. O principal problema com goto é que geralmente é muito poderoso. Na medida em que o goto atual é menos poderoso do que Dijkstra ‘ s, sua resposta está bem para mim.
- @MuhammadAlkarouri: Concordo totalmente. Você acabou de encontrar uma redação melhor para expressar exatamente o meu conceito. Meu ponto é que essas restrições às vezes podem não ser aplicáveis e o tipo de restrição de que você precisa não está no idioma. Então, algo mais ” poderoso “, menos especializado, é o que torna você capaz de contornar o problema. Por exemplo, o Java
break n
não está disponível em C ++, portantogoto escape
, mesmo seescape
não é necessário estar apenas fora do loop n-ple.
Resposta
O ímpar goto aqui ou lá, contanto que seja local para uma função, raramente prejudica significativamente a legibilidade. Muitas vezes, é benéfico ao chamar a atenção para o fato de que há algo incomum acontecendo neste código que requer o uso de uma estrutura de controle um tanto incomum .
Se os gotos (locais) prejudicam significativamente a legibilidade, geralmente é um sinal de que a função que contém o goto se tornou muito complexa.
O último goto que coloquei em um parte do código C era construir um par de loops interligados. Não se encaixa na definição normal de uso goto “aceitável”, mas a função acabou significativamente menor e mais clara como resultado. Para evitar o goto, seria necessária uma violação particularmente complicada de DRY.
Comentários
- A resposta para isso é declarada de forma mais contundente no antigo ” Lay ‘ s Batatas fritas ” slogan: ” Aposto que você pode ‘ comer apenas um “. Em seu exemplo, você tem dois ” interligados ” (o que quer que isso signifique, e eu ‘ m apostando que não são ‘ t loops bonitos, e o goto deu a você uma saída barata. E o cara da manutenção, que tem que modificar aquele código depois que você foi atropelado por um ônibus? O goto vai tornar sua vida mais fácil ou mais difícil? Esses dois loops precisam ser repensados?
Resposta
Acho que todo esse problema foi um caso de latido a árvore errada.
O GOTO, como tal, não parece problemático para mim, mas, muitas vezes, é um sintoma de um pecado real: código espaguete.
Se o GOTO causar O cruzamento de linhas de controle de fluxo é ruim, ponto final. Se não cruzar nenhuma linha de controle de fluxo, é inofensivo. Na zona cinzenta entre nós temos coisas como salvamentos de loop, ainda existem algumas linguagens que não adicionaram construções que cobrem todos os casos cinzentos legítimos.
O único caso em que me descobri realmente usando isso em muitos anos é o caso do loop em que o ponto de decisão está no meio do loop. Você fica com um código duplicado, um sinalizador ou uma GOTO. Acho a solução GOTO a melhor das três. Não há cruzamento de linhas de controle de fluxo aqui, é inofensivo.
Comentários
- O caso ao qual você alude é algumas vezes apelidado de “loop and a half” ou “N plus one half loop” e é um caso de uso famoso para
goto
s que entram em um loop (se a linguagem permitir; já que o outro caso, pular do loop no meio, geralmente é trivial semgoto
). - (Infelizmente, a maioria das linguagens proíbe pular em um loop.)
- @KonradRudolph: Se você implementar o ” loop ” com GOTO não há outra estrutura de loop para a qual saltar.
- Eu penso em
goto
gritando O FLUXO DE CONTROLE AQUI NÃO ‘ SE ADEQUA À PROGRAMAÇÃO ESTRUTURADA NORMAL PADRÕES . Como os fluxos de controle que se ajustam a padrões de programação estruturados são mais fáceis de raciocinar do que aqueles que não ‘ t, deve-se tentar usar tais padrões quando possível; se o código se ajusta a esses padrões, não se deve ‘ gritar que não ‘ t. Por outro lado, nos casos em que o código realmente não ‘ se encaixa nesses padrões, gritar sobre isso pode ser melhor do que fingir que o código se encaixa quando realmente não ‘ t. - @supercat Bem, imagine que você escreveu uma programação para si mesmo e então o vento o tira de suas mãos. De acordo com sua lógica, você não deve ‘ recuperá-lo, porque perseguir sua programação não estava ‘ dentro de sua programação.
Resposta
Sim, o goto pode ser usado para beneficiar a experiência do desenvolvedor: http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/
No entanto, assim como com qualquer ferramenta poderosa (ponteiros, herança múltipla, etc.), é preciso ser disciplinado usando isso. O exemplo fornecido no link usa PHP, que restringe o uso da construção goto à mesma função / método e desativa a capacidade de saltar para um novo bloco de controle (por exemplo, loop, instrução switch, etc.)
Resposta
Depende do idioma. Ele ainda é amplamente usado na programação Cobol, por exemplo. Eu também trabalhei em um dispositivo Barionet 50, cuja linguagem de programação de firmware é um dialeto BASIC antigo que, claro, requer o uso de Goto.
Resposta
goto
pode ser útil ao portar o código assembler legado para C. Na primeira instância, uma instrução a conversão por instrução para C, usando goto
como um substituto para a instrução assembler “s branch
, pode permitir uma portabilidade muito rápida.
Comentários
- Correto, mas um dos argumentos contra
goto
s é que eles impedem as otimizações do compilador. - @Sapphire_Brick: muitas otimizações de compilador são projetadas em torno de certos padrões de execução. Se a tarefa a ser realizada não ‘ não se encaixa nos padrões de execução suportados, ” frustrar ” otimizações podem não ser uma coisa ruim.
Resposta
Eu diria que não. Eles devem sempre ser substituídos por uma ferramenta mais específica para o problema que o goto está sendo usado para resolver (a menos que não esteja disponível em seu idioma). Por exemplo, instruções break e exceções resolvem problemas previamente resolvidos de goto de escape de loop e tratamento de erros.
Comentários
- Eu ‘ d defendo que quando os idiomas têm ambos esses recursos,
goto
está normalmente ausente desses idiomas. - Mas é porque eles não ‘ não precisa deles ou porque pessoas pensam que não ‘ não precisam deles?
- Suas opiniões são fanáticas. Há muitos casos em que
goto
é a coisa mais próxima da semântica do domínio do problema, qualquer outra coisa seria um hack sujo. - @ SK-logic: I don ‘ Não pense que a palavra ” fanático ” significa o que você acha que significa.
- @Keith,
devotado intolerantemente às próprias opiniões e preconceitos ” – que ‘ é o que eu observe consistentemente aqui, com este lote de olhos vendados indo para a frente. ” Deve sempre ser substituído ” são fanáticos ‘ s palavras, pelo menos. Nada perto de uma opinião adequada e sólida, mas apenas puro preconceito. Isso ” sempre ” não deixa espaço para nenhuma opinião alternativa, entende?
Resposta
Eu diria que não. Se você achar que é necessário usar GOTO, aposto que há necessidade de redesenhar o código.
Comentários
- Talvez, mas você não ‘ t forneceu qualquer raciocínio
- Dijkstra forneceu o raciocínio, em detalhes, décadas atrás.
- Apenas dogma. Sem ressonâncias. Nota: Djikstra NÃO É MAIS UMA RESPOSTA VÁLIDA: referia-se à instrução BASIC ou FORTRAN GOTO dos primeiros ‘ 60s. Eles não têm nada a ver com os realmente anêmicos (em comparação com o poder daqueles) gots-s de hoje.
- @EmilioGaravaglia Bem, a parte de seu raciocínio que ainda se aplica hoje é que ‘ é muito fácil escrever código espaguete usando a maneira de muitos gotos. No entanto, por essa lógica, não devemos ‘ usar ponteiros porque usar tantos ponteiros quanto
number of gotos needed to create problems
criaria um caos semelhante.
goto
é o que a CPU faz no final das contas, mas não funciona bem com o que os humanos precisam compreender e abstrair devido ao fato de não haver marcas no alvo fim. Compare com IntercalCOMEFROM
.goto
não é necessário. É apenas a maneira mais racional de implementá-los nas linguagens imperativas, uma vez que é a coisa mais próxima da própria semântica da transição de estado. E seu destino pode estar bem longe da fonte, ele não ‘ atrapalhará a legibilidade. Meu exemplo favorito desse código é D.E. Knuth ‘ s implementação do jogo Adventure.