O uso do goto vale a pena?


29

gotoé quase universalmente desencorajado. Vale a pena usar essa declaração?



1
gotoé o que a CPU faz, em última análise, mas não funciona bem com o que os humanos precisam compreender e abstrair porque não há marca no alvo. Compare com Intercal COMEFROM.

2
@ ThorbjørnRavnAndersen, o que mais os humanos querem dizer quando estão falando sobre transições de estado em uma máquina de estado? Você não vê uma semelhança? "Vá para um determinado estado ", é isso que significa e qualquer outra coisa seria apenas uma complicação desnecessária de uma coisa tão trivial. E o que você quer dizer com "sem marca"? Rótulo é uma marca.
SK-logic

@ SK-logic, depende de quão longe você permitirá que os goto venham. Máquinas de estado não exigem que o goto seja implementado.

@ ThorbjørnRavnAndersen, gotoé claro que não é necessário. É apenas a maneira mais racional de implementá-las nas linguagens imperativas, já 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, não prejudicará a legibilidade. Meu exemplo favorito desse código é a implementação de DE Knuth do jogo Adventure.
SK-logic

Respostas:


49

Isso foi discutido várias vezes no Stack Overflow, e Chris Gillum resumiu os possíveis usos degoto :

Sair de uma função corretamente

Geralmente, em uma função, você pode alocar recursos e precisar sair em vários locais. 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 o rótulo de limpeza. Dessa forma, você não precisa escrever um código de limpeza em todos os "pontos de saída" da função.

Saindo de loops aninhados

Se você estiver em um loop aninhado e precisar interromper todos os loops, um goto pode tornar isso muito mais limpo e mais simples do que as instruções break e if-checks.

Melhorias de baixo nível de desempenho

Isso é válido apenas no código perf-critical, mas as instruções goto são executadas muito rapidamente e podem dar um impulso ao se mover através de uma função. Essa é uma faca de dois gumes, no entanto, porque um compilador normalmente não pode otimizar código que contém gotos.

Eu argumentaria, como muitos outros argumentariam, que em todos esses casos, o uso de gotoé usado como um meio de sair de um canto em que um codificado foi inserido e geralmente é um sintoma de código que pode ser refatorado.


28
O primeiro problema é resolvido muito bem por finallyblocos nas línguas modernas, e o segundo é resolvido por breaks. No entanto, gotose você está preso ao C, é praticamente a única maneira de resolver esses problemas com elegância.
Chinmay Kanchi

2
Muito bons pontos. Eu gosto de como Java permite romper com vários níveis de loops
Casebash

4
@Chinmay - finalmente os blocos se aplicam apenas a 1) idiomas modernos que os possuem eb) você pode tolerar a sobrecarga (o tratamento de exceções tem uma sobrecarga.) Ou seja, o uso finallyé válido apenas nessas condições. Existem situações muito raras, mas válidas, em que um goto é o caminho a percorrer.
Luis.espinal

21
Ok pessoal ... qual é a diferença entre break 3ou break myLabele goto myLabel? Ah, isso mesmo, apenas a palavra-chave. Se você estiver usando break, continueou alguma palavra-chave semelhante você está infact usando um goto(se você estiver usando for, while, foreach, do/loopvocê estiver usando um goto condicional.)
Matthew Whited

4
Sobre o desempenho de baixo nível - o comportamento do processador moderno pode ser difícil de prever (pipeline, execução fora de ordem); portanto, provavelmente em 99% dos casos não vale a pena ou pode ser mais lento. @MatthewWhited: definição do escopo. Se você inserir o escopo em um ponto arbitrário, não está claro (para humanos) como os construtores devem ser chamados (o problema também existe em C).
Maciej Piechotka

42

As construções de fluxo de controle de nível superior tendem a corresponder aos 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é uma declaração de pausa diz "estávamos fazendo isso repetidamente, mas agora precisamos parar".

Uma declaração goto, por outro lado, tende a corresponder a um conceito no programa em execução, não no domínio do problema. Ele diz para continuar a execução em um ponto especificado no programa . Alguém que lê o código precisa inferir o que isso significa em relação 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 são apenas gotos disfarçados. Pense neles como gotos restritos - e são as restrições que os tornam úteis. Uma declaração de interrupção é implementada como um salto para o final do loop fechado, mas é melhor pensar em operar no loop como um todo.

Sendo tudo 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 declaração goto seja absolutamente necessária (há um teorema nesse sentido), mas há casos em que pode ser a solução menos ruim. Esses casos variam de idioma para idioma, dependendo das construções de nível superior que o idioma suporta.

Em C, por exemplo, acredito que há três cenários básicos em que um goto é apropriado.

  1. Rompendo um loop aninhado. Isso seria desnecessário se o idioma tivesse uma declaração de interrupção rotulada.
  2. Resgate de um trecho de código (normalmente um corpo de função) em caso de erro ou outro evento inesperado. Isso seria desnecessário se o idioma tivesse exceções.
  3. Implementando uma máquina explícita de estados finitos. Nesse caso (e, creio, apenas neste caso), um goto corresponde diretamente a um conceito no domínio do problema, passando de um estado para outro especificado, onde o estado atual é representado pelo bloco de código em execução no momento .

Por outro lado, uma máquina explícita de estados finitos também pode ser implementada com uma instrução switch dentro de um loop. Isso tem a vantagem de que todo estado inicia no mesmo local no código, o que pode ser útil para depuração, por exemplo.

O principal uso de um goto em uma linguagem razoavelmente moderna (que suporta if / else e loops) é simular uma construção de fluxo de controle que está faltando na linguagem.


5
Esta é realmente a melhor resposta até agora - bastante surpreendente, para uma pergunta que tem um ano e meio de idade. :-) #
31780 Konrad Rudolph

1
+1 a "melhor resposta até agora". --- "O principal uso de um goto em uma linguagem razoavelmente moderna (que suporta if / else e loops) é simular uma construção de fluxo de controle que está faltando na linguagem."
Dave Dopson

Na máquina de estado da instrução switch, toda gravação no valor de controle é realmente a goto. Os SSSM geralmente são boas estruturas para usar, pois separam o estado SM da construção de execução, mas na verdade não eliminam "gotos".
Supercat

2
"Tudo o resto é igual, código cuja estrutura reflete a do domínio do problema tende a ser mais fácil de ler e manter." Eu já sabia disso há muito tempo, mas faltava as palavras para comunicá-lo com tanta precisão de uma maneira que outros programadores possam e realmente entenderão. Obrigado por isso.
Wildcard

O "teorema do programa estruturado" me diverte, pois tende a demonstrar claramente o ponto de que o uso de instruções de programação estruturada para emular um goto é muito menos legível do que apenas usar um goto!

11

Certamente depende da linguagem de programação. A principal razão da gotocontrovérsia é por causa dos efeitos nocivos que surgem quando o compilador permite que você o use com muita liberalidade. Podem surgir problemas, por exemplo, se ele permite que você use de gotomaneira que agora você possa acessar uma variável não inicializada, ou pior, para pular para outro método e mexer na pilha de chamadas. Deve ser responsabilidade do compilador proibir o fluxo de controle sem sentido.

Java tentou "resolver" esse problema, impedindo gotocompletamente. No entanto, o Java permite que você use returndentro de um finallybloco e, portanto, faz com que uma exceção seja engolida inadvertidamente. O mesmo problema ainda está lá: o compilador não está fazendo seu trabalho. A remoção gotodo idioma não foi corrigida.

Em C #, gotoé tão segura como break, continue, try/catch/finallye return. Ele não permite que você use variáveis ​​não inicializadas, não deixa você pular de um bloco finalmente etc. O compilador irá reclamar. Isso ocorre porque resolve o problema real , como eu disse no fluxo de controle sem sentido. gotonão cancela magicamente a análise de atribuição definida e outras verificações razoáveis ​​do compilador.


Seu ponto é que c # gotonão é mau? Se assim for, que é o caso para cada linguagem moderna rotineiramente usado com uma gotoconstrução, não só C # ...
lvella

9

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 propenso a erros. Nestes casos, gotoé simplesmente melhor.

Obviamente, a breakdeclaração rotulada de Java faz a mesma coisa, mas sem permitir que você pule para um ponto arbitrário no código, que resolve o problema perfeitamente, sem permitir as coisas que fazem gotomal.


Alguém se importa em explicar o voto negativo? Esta foi uma resposta perfeitamente válida para a questão ...
Chinmay Kanchi

4
Goto nunca é uma resposta elegante. Quando seus loops estão aninhados em vários níveis, seu problema é que você não conseguiu arquitetar seu código para evitar os loops multinestados. Chamadas de procedimento NÃO são o inimigo. (Leia os artigos "Lambda: The Ultimate ...".) Usar um goto para romper loops aninhados a vários níveis de profundidade é colocar um curativo em uma fratura múltipla aberta: pode ser simples, mas não é o certo responda.
John R. Strohm

6
E, claro, nunca há um caso estranho quando são necessários loops profundamente aninhados? Ser um bom programador não é apenas seguir as regras, mas também saber quando quebrá-las.
Chinmay Kanchi

2
@ JohnR.Strohm Nunca diga nunca. Se você usa termos como "nunca" na programação, claramente não lida com casos extremos, otimização, restrições de recursos, etc. Em loops profundamente aninhados, o goto é realmente a maneira mais limpa e elegante de sair dos loops. Não importa o quanto você refatorou seu código.
Sujay Phadke

@ JohnR.Strohm: Segunda característica mais perdida na mudança de VB.NET para C #: o Doloop. Por quê? Porque eu posso mudar o loop que precisa de uma quebra profunda em um Doloop e digitar Exit Doe não há GoTo. Loops aninhados acontecem o tempo todo. Quebrar as pilhas acontece algumas vezes.
Joshua

7

A maior parte do desânimo vem de uma espécie de "religião" criada em meio a Deus Djikstra, que foi convincente no início dos anos 60 sobre 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
  • afaste-se de qualquer bloco de código sem qualquer limpeza possível.

Isso não tem mais nada a ver com a gotodeclaração das linguagens modernas, cuja existência se deve apenas ao suporte à criaçã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ê gotosair de um bloco, a pilha será desenrolada adequadamente e todos os destruidores adequados serão chamados)

Você pode consultar esta resposta para ter uma idéia de como mesmo o código que não usa goto pode ser ilegível. O problema não é ir sozinho, mas o mau uso dele.

Eu posso escrever um programa inteiro sem usar if, apenas for. Obviamente, 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 observando mais de máscaras goto para escapar longe das cimitarras dos fanáticos Djikstrarian, que -50 anos depois da invenção da laguages- moderna ainda querem que seus prisioneiros. Eles esqueceram o que Djikstra estava falando, lembram-se apenas do título de sua nota (GOTO considerado prejudicial, e nem era o título dele: foi alterado pelo editor) e culpar e bash, bash e culpar todos os contrutos que possuem aqueles 4 carta colocada em sequência.

Chegou o ano de 2011: é hora de entender que é gotopreciso notar a GOTOafirmação de que Djikstra era convincente.


1
"Coisas como quebrar, continuar, jogar, bool necessário = true; enquanto (necessário) {...}, etc. estão notando mais do que mascarar o goto" Eu não concordo com isso. Eu preferiria "coisas como pausa etc são restritas, goto", ou algo assim. O principal problema com o goto é que ele geralmente é muito poderoso. Na medida em que o atual goto é menos poderoso do que o de Dijkstra, sua resposta está bem comigo.
Muhammad Alkarouri

2
@MuhammadAlkarouri: Eu concordo totalmente. Você acabou de encontrar uma formulação melhor para expressar exatamente o meu conceito. Meu argumento é que essas restrições às vezes podem não ser aplicáveis ​​e o tipo de restrição que você precisa não está no idioma. Então, uma coisa mais "poderosa", menos especializada, é o que o torna capaz de solucionar o problema. Por exemplo, o Java break nnão está disponível em C ++, portanto goto escape, mesmo que escapenão seja necessário sair do loop n-ple.
Emilio Garavaglia

4

O goto estranho aqui ou ali, desde que seja local para uma função, raramente prejudica significativamente a legibilidade. Muitas vezes, é benéfico chamando a atenção para o fato de que há algo incomum nesse código que requer o uso de uma estrutura de controle um tanto incomum.

Se os gotos (locais) estão prejudicando significativamente a legibilidade, geralmente é um sinal de que a função que contém o goto se tornou muito complexa.

O último passo que eu coloquei em um pedaço de código C foi construir um par de loops interligados. Não se encaixa na definição normal de uso "aceitável" do goto, mas a função acabou sendo significativamente menor e mais clara como resultado. Para evitar o goto, seria necessária uma violação particularmente desordenada do DRY.


1
A resposta para isso é mais expressiva no antigo slogan "Lay's Potato Chips": "Aposto que você não pode comer apenas um". No seu exemplo, você tem dois loops "intertravados" (o que quer que isso signifique, e aposto que não é bonito), e o goto deu a você uma saída barata. E o cara da manutenção, que precisa modificar esse código depois que você é atropelado por um ônibus? O goto vai tornar sua vida mais fácil ou mais difícil? Esses dois loops precisam ser repensados?
John R. Strohm

3

Eu acho que toda essa questão foi um caso de latir na árvore errada.

GOTO, como tal, não me parece problemático, mas muitas vezes é um sintoma de um pecado real: código de espaguete.

Se o GOTO causar uma grande travessia das linhas de controle de fluxo, está ruim, ponto final. Se não cruzar nenhuma linha de controle de fluxo, é inofensivo. Na zona cinza entre nós temos coisas como salvamentos em loop, ainda existem alguns idiomas que não adicionaram construções que cobrem todos os casos cinza legítimos.

O único caso em que eu realmente o uso em muitos anos é o caso do loop em que o ponto de decisão está no meio do loop. Você fica com código duplicado, uma bandeira ou um GOTO. Acho a solução GOTO a melhor das três. Não há cruzamento de linhas de controle de fluxo aqui, é inofensivo.


O caso ao qual você alude é às vezes apelidado de “loop e meio” ou “N mais um meio loop” e é um famoso caso de uso para gotos que pulam em um loop (se o idioma permitir; se o idioma permitir; já no outro caso, pular fora do circuito no meio, geralmente é trivial sem goto).
21978 Konrad Rudolph

(Infelizmente, a maioria das linguagens proíbem que salta em forma de laço.)
de Konrad Rudolph

@KonradRudolph: Se você implementar o "loop" com o GOTO, não haverá outra estrutura de loop na qual entrar.
Loren Pechtel

1
Penso gotoem gritar: O FLUXO DE CONTROLE AQUI NÃO SE ENCONTRA PADRÕES NORMAIS DE PROGRAMAÇÃO ESTRUTURADA . Como os fluxos de controle que se ajustam aos padrões de programação estruturados são mais fáceis de raciocinar do que aqueles que não o fazem, deve-se tentar usar esses padrões sempre que possível; se o código se encaixa nesses padrões, não se deve gritar que não. 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 se encaixa.
Super27 /

1

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-a. O exemplo fornecido no link usa PHP, que restringe o uso da construção goto para a mesma função / método e desativa a capacidade de saltar para um novo bloco de controle (por exemplo, loop, instrução switch, etc.)


1

Depende do idioma. Ainda é amplamente utilizado na programação Cobol, por exemplo. Também trabalhei em um dispositivo Barionet 50, cuja linguagem de programação de firmware é um dialeto BASIC antigo que, naturalmente, exige que você use Goto.


0

Eu diria que não. Se você achar necessário usar o GOTO, aposto que é necessário redesenhar o código.


3
Talvez, mas você não forneceu nenhum raciocínio
Casebash 02/09

Dijkstra forneceu o raciocínio, em detalhes, décadas atrás.
John R. Strohm

2
Apenas Dogma. Sem resons. Nota: Djikstra NÃO É MAIS UMA RESIDÊNCIA VÁLIDA: estava se referindo à declaração BASIC ou FORTRAN GOTO do início dos anos 60. Eles não têm nada a ver com os realmente anêmicos (em comparação com o poder daqueles) gots-s de hoje.
Emilio Garavaglia

0

gotopode ser útil ao portar o código do assembler legado para C. Na primeira instância, uma conversão de instrução por instrução para C, usando gotocomo um substituto para a branchinstrução do assembler , pode permitir a portabilidade muito rápida.


-1

Eu diria que não. Eles sempre devem 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 no seu idioma). Por exemplo, instruções e exceções de quebra resolvem problemas anteriormente resolvidos para goto de escape de loop e manipulação de erros.


Eu diria que, quando os idiomas têm esses dois recursos, gotonormalmente eles estão ausentes.
Chinmay Kanchi

Mas é isso porque eles não precisam deles, ou porque as pessoas pensam que não precisam deles?
Michael K

Suas opiniões são preconceituosas. Existem muitos casos em que gotoa coisa mais próxima da semântica do domínio do problema é que qualquer outra coisa seria um hack sujo.
SK-logic

@ SK-logic: Eu não acho que a palavra "intolerante" significa o que você pensa que significa.
Keith Thompson

1
@ Keith, "intolerantemente devotado às suas próprias opiniões e preconceitos" - é o que eu sempre observo aqui, com esse monte de olhos vendados. "Devem sempre ser substituídos" são as palavras dos fanáticos, pelo menos. Nada perto de uma opinião adequada e sólida, mas apenas um puro fanatismo. Esse "sempre" não deixa espaço para nenhuma opinião alternativa, entende?
SK-logic
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.