Pergunta

Todo mundo está ciente do que Dijkstra Cartas para o editor:ir para declaração considerada prejudicial (também aqui transcrição .html e aqui .pdf) e tem havido um esforço formidável desde então para evitar a instrução goto sempre que possível.Embora seja possível usar goto para produzir código extenso e insustentável, ele ainda assim permanece em linguagens de programação modernas.Mesmo os avançados continuação a estrutura de controle no Scheme pode ser descrita como um goto sofisticado.

Que circunstâncias justificam o uso de goto?Quando é melhor evitar?

Como pergunta de acompanhamento:C fornece um par de funções, setjmp e longjmp, que fornecem a capacidade de ir para não apenas dentro do quadro de pilha atual, mas dentro de qualquer um dos quadros de chamada.Deveriam estes ser considerados tão perigosos quanto goto?Mais perigoso?


O próprio Dijkstra lamentou esse título, pelo qual não foi responsável.No fim de EWD1308 (também aqui .pdf) ele escreveu:

Finalmente um conto para registro.Em 1968, as comunicações da ACM publicaram um texto meu sob o título "A declaração goto considerada prejudicial", que nos anos posteriores seria mais frequentemente referenciado, lamentavelmente, no entanto, muitas vezes por autores que não haviam visto mais do que seu título, que se tornou uma pedra angular da minha fama ao se tornar um modelo:Vimos todos os tipos de artigos sob o título "X considerado prejudicial" para quase qualquer X, incluindo um intitulado "Dijkstra considerado prejudicial".Mas o que aconteceu?Eu tinha enviado um artigo sob o título "Um caso contra a declaração Goto", que, para acelerar sua publicação, o editor havia mudado para uma" carta para o editor "e, no processo, ele havia lhe dado um novo título de sua própria invenção!O editor era Niklaus Wirth.

Um artigo clássico bem pensado sobre este tópico, que pode ser comparado ao de Dijkstra, é Programação estruturada com instruções go to, por Donald E.Knuth.A leitura ajuda a restabelecer o contexto e uma compreensão não dogmática do assunto.Neste artigo, a opinião de Dijkstra sobre este caso é relatada e é ainda mais forte:

Donald E.Knuth: Acredito que, ao apresentar tal visão, não estou de fato discordando bruscamente com as idéias de Dijkstra, já que ele escreveu recentemente o seguinte:"Por favor, não caia na armadilha de acreditar que eu sou terrivelmente dogmático sobre [ir para a declaração]. Tenho a sensação desconfortável de que outros estão fazendo uma religião com isso, como se os problemas conceituais de programação pudessem ser resolvidos por um único truque, por uma forma simples de disciplina de codificação!"

Foi útil?

Solução

As afirmações a seguir são generalizações;embora seja sempre possível alegar exceção, geralmente (na minha experiência e humilde opinião) não vale a pena correr os riscos.

  1. O uso irrestrito de endereços de memória (seja GOTO ou ponteiros brutos) oferece muitas oportunidades para cometer erros facilmente evitáveis.
  2. Quanto mais maneiras existirem para chegar a um determinado "local" no código, menos confiante se poderá estar sobre qual é o estado do sistema naquele ponto.(Veja abaixo.)
  3. A programação estruturada IMHO tem menos a ver com "evitar GOTOs" e mais com fazer com que a estrutura do código corresponda à estrutura dos dados.Por exemplo, uma estrutura de dados repetida (por exemploarray, arquivo sequencial, etc.) é naturalmente processado por uma unidade repetida de código.Ter estruturas incorporadas (por ex.while, for, Until, for-each, etc.) permite ao programador evitar o tédio de repetir os mesmos padrões de código clichê.
  4. Mesmo que GOTO seja um detalhe de implementação de baixo nível (nem sempre é o caso!), Está abaixo do nível que o programador deveria estar pensando.Quantos programadores equilibram seus talões de cheques pessoais em binário bruto?Quantos programadores se preocupam com qual setor do disco contém um registro específico, em vez de apenas fornecer uma chave para um mecanismo de banco de dados (e de quantas maneiras as coisas poderiam dar errado se realmente escrevêssemos todos os nossos programas em termos de setores de disco físico)?

Notas de rodapé acima:

Em relação ao ponto 2, considere o seguinte código:

a = b + 1
/* do something with a */

No ponto “faça alguma coisa” do código, podemos afirmar com grande confiança que a é melhor que b.(Sim, estou ignorando a possibilidade de estouro de número inteiro não capturado.Não vamos nos prender a um exemplo simples.)

Por outro lado, se o código fosse lido desta forma:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

A multiplicidade de maneiras de chegar ao rótulo 10 significa que temos que trabalhar muito mais para ter confiança nas relações entre a e b nesse ponto.(Na verdade, no caso geral é indecidível!)

Em relação ao ponto 4, toda a noção de “ir a algum lugar” no código é apenas uma metáfora.Nada realmente "vai" para qualquer lugar dentro da CPU, exceto elétrons e fótons (para o calor desperdiçado).Às vezes desistimos de uma metáfora por outra mais útil.Lembro-me de ter encontrado (algumas décadas atrás!) uma linguagem onde

if (some condition) {
  action-1
} else {
  action-2
}

foi implementado em uma máquina virtual compilando action-1 e action-2 como rotinas sem parâmetros fora de linha e, em seguida, usando um único opcode VM de dois argumentos que usou o valor booleano da condição para invocar um ou outro.O conceito era simplesmente “escolher o que invocar agora” em vez de “ir aqui ou ali”.Novamente, apenas uma mudança de metáfora.

Outras dicas

XKCD's GOTO Comic

Um colega de trabalho meu disse que a única razão para usar um GOTO é se você se programou tão longe que essa é a única saída.Em outras palavras, projete adequadamente com antecedência e você não precisará usar um GOTO posteriormente.

Eu pensei que essa história em quadrinhos ilustra que "eu poderia reestruturar o fluxo do programa ou usar um pequeno 'goto'". Um goto é uma saída fraca quando você tem um design fraco. Velociraptores atacam os fracos.

Às vezes é válido usar GOTO como alternativa ao tratamento de exceções dentro de uma única função:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

O código COM parece cair nesse padrão com bastante frequência.

Só me lembro de usar goto uma vez.Eu tinha uma série de cinco loops contados aninhados e precisava ser capaz de romper toda a estrutura por dentro com base em certas condições:

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

Eu poderia facilmente ter declarado uma variável de interrupção booleana e usá-la como parte da condicional para cada loop, mas neste caso decidi que um GOTO era igualmente prático e legível.

Nenhum velociraptor me atacou.

Nós já tivemos isso discussão e eu fico parado meu ponto.

Além disso, estou farto de pessoas que descrevem estruturas linguísticas de nível superior como “goto disfarçado” porque eles claramente não entenderam de forma alguma.Por exemplo:

Até mesmo a estrutura avançada de controle de continuação no Scheme pode ser descrita como um goto sofisticado.

Isso é um absurdo completo. Todo estrutura de controle pode ser implementada em termos de goto mas esta observação é totalmente trivial e inútil. goto não é considerado prejudicial pelos seus efeitos positivos, mas sim pelas suas consequências negativas, que foram eliminadas pela programação estruturada.

Da mesma forma, dizer “GOTO é uma ferramenta e, como todas as ferramentas, pode ser usada e abusada” é completamente errado.Nenhum trabalhador da construção moderno usaria uma rocha e reivindicaria que "é uma ferramenta". Rochas foram substituídas por martelos. goto foi substituído por estruturas de controle.Se o trabalhador da construção civil ficasse preso na natureza sem um martelo, é claro que ele usaria uma pedra.Se um programador tiver que usar uma linguagem de programação inferior que não tenha o recurso X, bem, é claro que ele terá que usar goto em vez de.Mas se ela o usar em qualquer outro lugar, em vez do recurso de idioma apropriado, ela claramente não entendeu o idioma corretamente e o usa de maneira errada.É tão simples assim.

Goto está extremamente abaixo na minha lista de coisas para incluir em um programa apenas por fazer.Isso não significa que seja inaceitável.

Goto pode ser bom para máquinas de estado.Uma instrução switch em um loop é (em ordem de importância típica):(a) não é realmente representativo do fluxo de controle, (b) feio, (c) potencialmente ineficiente dependendo da linguagem e do compilador.Então você acaba escrevendo uma função por estado e fazendo coisas como "Return Next_State;" que até se parecem com Goto.

É verdade que é difícil codificar máquinas de estado de uma forma que as torne fáceis de entender.No entanto, nenhuma dessas dificuldades tem a ver com o uso de goto, e nada disso pode ser reduzido com o uso de estruturas de controle alternativas.A menos que sua linguagem tenha uma construção de 'máquina de estado'.O meu não.

Nas raras ocasiões em que seu algoritmo é realmente mais compreensível em termos de um caminho através de uma sequência de nós (estados) conectados por um conjunto limitado de transições permitidas (gotos), em vez de qualquer fluxo de controle mais específico (loops, condicionais, outros enfeites ), então isso deve estar explícito no código.E você deveria desenhar um diagrama bonito.

setjmp/longjmp pode ser bom para implementar exceções ou comportamento semelhante a exceção.Embora não sejam universalmente elogiadas, as exceções são geralmente consideradas uma estrutura de controle “válida”.

setjmp/longjmp são 'mais perigosos' do que goto no sentido de que são mais difíceis de usar corretamente, muito menos de forma compreensível.

Nunca houve, nem jamais haverá, qualquer idioma em que seja o menos difícil de escrever um código ruim.-Donald Knuth.

Tirar goto de C não tornaria mais fácil escrever um bom código em C.Na verdade, preferiria não perceber que C é suposto ser capaz de atuar como uma linguagem assembly glorificada.

Em seguida, serão "ponteiros considerados prejudiciais" e depois "digitação de pato considerada prejudicial".Então quem sobrará para defendê-lo quando vierem tirar sua construção de programação insegura?Eh?

Em Linux:Usando goto no código do kernel no Kernel Trap, há uma discussão com Linus Torvalds e um "cara novo" sobre o uso de GOTOs no código Linux.Há alguns pontos muito bons aí e Linus vestido com aquela arrogância de sempre :)

Algumas passagens:

Linus:"Não, você sofreu uma lavagem cerebral por pessoas da CS que pensavam que Niklaus Wirth realmente sabia do que estava falando.Ele não fez isso.Ele não tem uma pista louca. "

-

Linus:"Acho que os Goto's estão bem e geralmente são mais legíveis do que grandes quantidades de indentação".

-

Linus:"É claro que, em idiomas estúpidos como Pascal, onde os rótulos não podem ser descritivos, o Goto's pode ser ruim".

Em C, goto funciona apenas dentro do escopo da função atual, que tende a localizar possíveis bugs. setjmp e longjmp são muito mais perigosos, sendo não locais, complicados e dependentes da implementação.Na prática, porém, eles são muito obscuros e incomuns para causar muitos problemas.

Acredito que o perigo de goto em C é muito exagerado.Lembre-se que o original goto argumentos ocorreram na época de linguagens como o antigo BASIC, onde iniciantes escreviam código espaguete como este:

3420 IF A > 2 THEN GOTO 1430

Aqui Linus descreve um uso apropriado de goto: http://www.kernel.org/doc/Documentation/CodingStyle (Capítulo 7).

Hoje, é difícil ver o grande problema do GOTO declaração porque o pessoal da "programação estruturada" ganhou principalmente o debate e as linguagens de hoje têm estruturas de fluxo de controle suficientes para evitar GOTO.

Conte o número de gotos em um programa C moderno.Agora adicione o número de break, continue, e return declarações.Além disso, adicione o número de vezes que você usa if, else, while, switch ou case.Isso é sobre quantos GOTOComo seria o seu programa se você estivesse escrevendo em FORTRAN ou BASIC em 1968, quando Dijkstra escreveu sua carta.

As linguagens de programação da época careciam de fluxo de controle.Por exemplo, no Dartmouth BASIC original:

  • IF declarações não tiveram ELSE.Se você quisesse um, você tinha que escrever:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • Mesmo que seu IF declaração não precisava de um ELSE, ainda estava limitado a uma única linha, que geralmente consistia em um GOTO.

  • Não houve DO...LOOP declaração.Para não-FOR loops, você tinha que terminar o loop com um explícito GOTO ou IF...GOTO de volta ao início.

  • Não houve SELECT CASE.Você teve que usar ON...GOTO.

Então você acabou com um muito de GOTOestá no seu programa.E você não poderia depender da restrição de GOTOs dentro de uma única sub-rotina (porque GOSUB...RETURN era um conceito tão fraco de sub-rotinas), então estes GOTOpoderia ir em qualquer lugar.Obviamente, isso tornou o fluxo de controle difícil de seguir.

É aqui que o anti-GOTO veio o movimento.

Go To pode fornecer uma espécie de substituto para o tratamento de exceções "real" em certos casos.Considerar:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

Obviamente este código foi simplificado para ocupar menos espaço, então não se preocupe muito com os detalhes.Mas considere uma alternativa que já vi muitas vezes Produção código por codificadores indo a extremos absurdos para evitar o uso de goto:

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

Agora, funcionalmente, esse código faz exatamente a mesma coisa.Na verdade, o código gerado pelo compilador é quase idêntico.No entanto, no zelo do programador em apaziguar Nogoto (o temido deus da repreensão acadêmica), este programador quebrou completamente a linguagem subjacente que o while loop representa e fez um número real na legibilidade do código. Isto não é melhor.

Então, a moral da história é: se você estiver recorrendo a algo realmente estúpido para evitar o uso de goto, não faça isso.

Donald E.Knuth respondeu a esta pergunta no livro "Literate Programming", 1992 CSLI.Na pág.17 há um ensaio "Programação estruturada com instruções goto" (PDF).Acho que o artigo também pode ter sido publicado em outros livros.

O artigo descreve a sugestão de Dijkstra e descreve as circunstâncias em que esta é válida.Mas ele também dá uma série de contra-exemplos (problemas e algoritmos) que não podem ser facilmente reproduzidos usando apenas loops estruturados.

O artigo contém uma descrição completa do problema, o histórico, exemplos e contra-exemplos.

Atraído por Jay Ballou adicionando uma resposta, adicionarei meus £ 0,02.Se Bruno Ranschaert ainda não tivesse feito isso, eu teria mencionado o artigo "Programação Estruturada com Instruções GOTO" de Knuth.

Uma coisa que não vi discutida é o tipo de código que, embora não seja exatamente comum, era ensinado nos livros didáticos de Fortran.Coisas como o alcance estendido de um loop DO e sub-rotinas de código aberto (lembre-se, isso seria Fortran II, ou Fortran IV, ou Fortran 66 - não Fortran 77 ou 90).Há pelo menos uma chance de que os detalhes sintáticos sejam inexatos, mas os conceitos devem ser suficientemente precisos.Os trechos em cada caso estão dentro de uma única função.

Observe que o excelente, mas datado (e esgotado) livro 'Os Elementos do Estilo de Programação, 2ª Ed.' por Kernighan & Plauger inclui alguns exemplos da vida real de abuso de GOTO em livros de programação de sua época (final dos anos 70).O material abaixo não é desse livro, entretanto.

Faixa estendida para um loop DO

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

Uma razão para tal absurdo foi o bom e velho cartão perfurado.Você pode notar que os rótulos (bem fora de sequência porque esse era o estilo canônico!) estão na coluna 1 (na verdade, eles tinham que estar nas colunas 1-5) e o código está nas colunas 7-72 ​​(a coluna 6 era a continuação coluna de marcadores).As colunas 73 a 80 receberiam um número de sequência, e havia máquinas que classificavam os baralhos de cartões perfurados em ordem de número de sequência.Se você tivesse seu programa em cartões sequenciados e precisasse adicionar alguns cartões (linhas) no meio de um loop, teria que refazer tudo após essas linhas extras.No entanto, se você substituísse um cartão pelo material GOTO, você poderia evitar re-sequenciar todos os cartões - você apenas colocava os novos cartões no final da rotina com novos números de sequência.Consideremos que esta é a primeira tentativa de “computação verde” – uma poupança de cartões perfurados (ou, mais especificamente, uma poupança de trabalho de redigitação – e uma poupança de erros de redigitação consequentes).

Ah, você também pode notar que estou trapaceando e não gritando - Fortran IV foi escrito normalmente em letras maiúsculas.

Sub-rotina de código aberto

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

O GOTO entre os rótulos 76 e 54 é uma versão do goto computado.Se a variável i tiver o valor 1, vá para o primeiro rótulo da lista (123);se tiver o valor 2, vá para o segundo e assim por diante.O fragmento de 76 ao goto calculado é a sub-rotina de código aberto.Era um trecho de código executado como uma sub-rotina, mas escrito no corpo de uma função.(Fortran também tinha funções de instrução - que eram funções incorporadas que cabiam em uma única linha.)

Havia construções piores que o goto calculado - você poderia atribuir rótulos às variáveis ​​e então usar um goto atribuído.Pesquisando no Google atribuído ir para me diz que foi excluído do Fortran 95.Marque a revolução da programação estruturada que poderia ser considerada como tendo começado em público com a carta ou artigo "GOTO considerado prejudicial" de Dijkstra.

Sem algum conhecimento do tipo de coisas que foram feitas em Fortran (e em outras linguagens, a maioria das quais caiu justamente no esquecimento), é difícil para nós, recém-chegados, compreender a extensão do problema com o qual Dijkstra estava lidando.Caramba, eu só comecei a programar dez anos depois da publicação daquela carta (mas tive a infelicidade de programar em Fortran IV por um tempo).

Goto considerado útil.

Comecei a programar em 1975.Para os programadores da década de 1970, as palavras “goto considerado prejudicial” diziam mais ou menos que valia a pena tentar novas linguagens de programação com estruturas de controle modernas.Nós tentamos os novos idiomas.Nós rapidamente convertemos.Nós nunca voltamos.

Nunca voltamos, mas, se você é mais jovem, então nunca esteve lá.

Agora, uma experiência em linguagens de programação antigas pode não ser muito útil, exceto como um indicador da idade do programador.No entanto, os programadores mais jovens não têm esta formação, pelo que já não compreendem a mensagem que o slogan “vá para considerado prejudicial” transmitia. ao público-alvo no momento em que foi apresentado.

Slogans que não entendemos não são muito esclarecedores.Provavelmente é melhor esquecer esses slogans.Esses slogans não ajudam.

Este slogan específico, no entanto, “Goto considerado prejudicial”, ganhou vida própria como morto-vivo.

Não pode ser abusado?Responder:claro, mas e daí?Praticamente todos os elementos de programação pode ser abusado.O humilde bool por exemplo, é abusado com mais frequência do que alguns de nós gostaríamos de acreditar.

Por outro lado, não consigo me lembrar de ter conhecido um único caso real de abuso de goto desde 1990.

O maior problema com goto provavelmente não é técnico, mas social.Programadores que não sabem muito às vezes parecem sentir que descontinuar goto os faz parecer inteligentes.Talvez você precise satisfazer esses programadores de vez em quando.Como a vida.

A pior coisa do goto hoje é que ele não é usado o suficiente.

Não existem coisas como GOTO considerado prejudicial.

GOTO é uma ferramenta e, como todas as ferramentas, pode ser usada e abusado.

Existem, no entanto, muitas ferramentas no mundo da programação que tendem a ser abusado mais do que ser usado, e GOTO é um deles.o COM declaração de Delphi é outra.

Pessoalmente também não uso em código típico, mas tive o uso estranho de ambos VÁ PARA e COM que eram garantidos e uma solução alternativa conteria mais código.

A melhor solução seria o compilador apenas avisar que a palavra-chave foi contaminado, e você teria que inserir algumas diretivas pragmáticas em torno da declaração para se livrar dos avisos.

É como dizer aos seus filhos para não corra com tesoura.Tesouras não são ruins, mas seu uso talvez não seja a melhor maneira de manter a saúde.

Desde que comecei a fazer algumas coisas no kernel do Linux, gotos não me incomoda tanto quanto antes.No começo fiquei meio horrorizado ao ver que eles (caras do kernel) adicionaram gotos ao meu código.Desde então, me acostumei com o uso de gotos, em alguns contextos limitados, e agora irei usá-los ocasionalmente.Normalmente, é um goto que vai para o final de uma função para fazer algum tipo de limpeza e resgate, em vez de duplicar a mesma limpeza e resgate em vários lugares da função.E normalmente, não é algo grande o suficiente para ser transferido para outra função – por exemplo.liberar algumas variáveis ​​​​localmente (k) mallocadas é um caso típico.

Escrevi código que usou setjmp/longjmp apenas uma vez.Estava em um programa sequenciador de bateria MIDI.A reprodução ocorreu em um processo separado de toda a interação do usuário, e o processo de reprodução usou memória compartilhada com o processo de UI para obter as informações limitadas necessárias para fazer a reprodução.Quando o usuário queria interromper a reprodução, o processo de reprodução apenas fazia um longjmp "de volta ao início" para recomeçar, em vez de um desenrolar complicado de onde quer que estivesse sendo executado quando o usuário queria que parasse.Funcionou muito bem, foi simples e nunca tive problemas ou bugs relacionados a ele nesse caso.

setjmp/longjmp tem seu lugar - mas esse lugar é aquele que você provavelmente não visitará, mas de vez em quando.

Editar:Acabei de olhar o código.Na verdade, foi siglongjmp() que eu usei, não longjmp (não que seja grande coisa, mas eu tinha esquecido que siglongjmp existia).

Nunca foi, desde que você fosse capaz de pensar por si mesmo.

Se você estiver escrevendo uma VM em C, acontece que usando gotos computados (do gcc) assim:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

funciona muito mais rápido que o switch convencional dentro de um loop.

Porque goto pode ser usado para confundir metaprogramação

Goto é tanto um alto nível e um nível baixo expressão de controle e, como resultado, simplesmente não possui um padrão de design apropriado para a maioria dos problemas.

Isso é nível baixo no sentido de que goto é uma operação primitiva que implementa algo superior como while ou foreach ou alguma coisa.

Isso é alto nível no sentido de que quando usado de certas maneiras, ele pega o código que é executado em uma sequência clara, de forma ininterrupta, exceto para loops estruturados, e o transforma em peças de lógica que são, com bastante gotos, um monte de lógica sendo remontada dinamicamente.

Então, há um prosaico e um mal lado para goto.

O lado prosaico é que um goto apontando para cima pode implementar um loop perfeitamente razoável e um goto apontando para baixo pode fazer um loop perfeitamente razoável break ou return.Claro, um verdadeiro while, break, ou return seria muito mais legível, pois o pobre humano não teria que simular o efeito do goto para ter uma visão geral.Então, uma má ideia em geral.

O lado mau envolve uma rotina que não usa goto para while, break ou return, mas usa-o para o que é chamado lógica do espaguete.Neste caso, o desenvolvedor feliz está construindo pedaços de código a partir de um labirinto de gotos, e a única maneira de entendê-lo é simulá-lo mentalmente como um todo, uma tarefa terrivelmente cansativa quando há muitos gotos.Quero dizer, imagine o problema de avaliar o código onde o else não é exatamente o inverso do if, onde aninhado ifs pode permitir algumas coisas que foram rejeitadas pelo exterior if, etc etc.

Finalmente, para realmente cobrir o assunto, devemos notar que essencialmente todas as línguas antigas, exceto Algol, inicialmente faziam apenas declarações únicas sujeitas às suas versões de if-then-else.Então, a única maneira de fazer um bloqueio condicional era goto em torno dele usando uma condicional inversa.Insano, eu sei, mas li algumas especificações antigas.Lembre-se de que os primeiros computadores foram programados em código de máquina binário, então suponho que qualquer tipo de HLL foi um salva-vidas;Acho que eles não foram muito exigentes sobre exatamente quais recursos HLL obtiveram.

Tendo dito tudo o que eu costumava enfiar um goto em cada programa que escrevi "só para irritar os puristas".

Negar o uso da instrução GOTO aos programadores é como dizer a um carpinteiro para não usar um martelo, pois isso pode danificar a parede enquanto ele martela um prego.Um verdadeiro programador sabe como e quando usar um GOTO.Eu segui alguns desses chamados 'Programas Estruturados'. Vi códigos tão horríveis apenas para evitar o uso de um GOTO, que eu poderia atirar no programador.Ok, em defesa do outro lado, eu também vi alguns códigos espaguete reais e, novamente, esses programadores também deveriam ser fuzilados.

Aqui está apenas um pequeno exemplo de código que encontrei.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------OU----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10

"Neste link http://kerneltrap.org/node/553/2131"

Ironicamente, eliminar o goto introduziu um bug:a chamada do spinlock foi omitida.

O artigo original deve ser considerado como "GOTO incondicional considerado prejudicial".Defendia, em particular, uma forma de programação baseada em condições condicionais (if) e iterativo (while) construções, em vez do teste e salto comum ao código inicial. goto ainda é útil em alguns idiomas ou circunstâncias, onde não existe uma estrutura de controle apropriada.

Sobre o único lugar em que concordo, vá para poderia ser usado é quando você precisa lidar com erros, e cada ponto específico em que ocorre um erro requer tratamento especial.

Por exemplo, se você estiver capturando recursos e usando semáforos ou mutexes, será necessário capturá-los em ordem e sempre liberá-los da maneira oposta.

Alguns códigos exigem um padrão muito estranho de captura desses recursos, e você não pode simplesmente escrever uma estrutura de controle de fácil manutenção e compreensão para lidar corretamente com a captura e a liberação desses recursos para evitar impasses.

É sempre possível fazer certo sem goto, mas neste caso e em alguns outros Goto é na verdade a melhor solução principalmente para legibilidade e manutenção.

-Adão

Um uso moderno do GOTO é feito pelo compilador C# para criar máquinas de estado para enumeradores definidos pelo retorno de rendimento.

GOTO é algo que deve ser usado por compiladores e não por programadores.

Até que C e C++ (entre outros culpados) tenham rotulado breaks e continue, goto continuará a ter uma função.

Se o próprio GOTO fosse mau, os compiladores seriam maus, porque geram JMPs.Se saltar para um bloco de código, especialmente seguindo um ponteiro, fosse inerentemente mau, a instrução RETurn seria má.Em vez disso, o mal está no potencial para abuso.

Às vezes, tive que escrever aplicativos que precisavam monitorar vários objetos em que cada objeto tinha que seguir uma intrincada sequência de estados em resposta a eventos, mas a coisa toda era definitivamente de thread único.Uma sequência típica de estados, se representada em pseudocódigo seria:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

Tenho certeza que isso não é novidade, mas a forma como lidei com isso em C(++) foi definindo algumas macros:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Então (assumindo que o estado é inicialmente 0) a máquina de estado estruturado acima se transforma no código estruturado:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

Com uma variação disso, pode haver CALL e RETURN, portanto algumas máquinas de estado podem atuar como sub-rotinas de outras máquinas de estado.

É incomum?Sim.É necessário algum aprendizado por parte do mantenedor?Sim.Esse aprendizado compensa?Eu penso que sim.Isso poderia ser feito sem GOTOs que saltam em blocos?Não.

Eu evito isso, pois um colega de trabalho/gerente sem dúvida questionará seu uso em uma revisão de código ou quando o encontrar.Embora eu ache que ele tem usos (o caso de tratamento de erros, por exemplo) - você entrará em conflito com algum outro desenvolvedor que terá algum tipo de problema com ele.

Não vale a pena.

Na verdade, fui forçado a usar goto, porque literalmente não conseguia pensar em uma maneira melhor (mais rápida) de escrever este código:

Eu tinha um objeto complexo e precisava fazer alguma operação nele.Se o objeto estivesse em um estado, eu poderia fazer uma versão rápida da operação, caso contrário, teria que fazer uma versão lenta da operação.Acontece que em alguns casos, no meio da operação lenta, foi possível perceber que isso poderia ter sido feito com a operação rápida.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

Isso ocorreu em uma parte crítica do código da interface do usuário em tempo real, então, honestamente, acho que um GOTO foi justificado aqui.

Hugo

Quase todas as situações em que um goto pode ser usado, você pode fazer o mesmo usando outras construções.Goto é usado pelo compilador de qualquer maneira.

Eu pessoalmente nunca o uso explicitamente, nem preciso.

Uma coisa que eu não vi qualquer das respostas aqui é que uma solução 'goto' é muitas vezes mais eficiente do que uma das soluções de programação estruturada frequentemente mencionadas.

Considere o caso de muitos loops aninhados, onde usar 'goto' em vez de um monte de if(breakVariable) seções é obviamente mais eficiente.A solução "Coloque seus loops em uma função e use return" geralmente é totalmente irracional.No caso provável de os loops usarem variáveis ​​locais, agora você terá que passá-los todos através de parâmetros de função, potencialmente lidando com muitas dores de cabeça extras que surgem disso.

Agora considere o caso de limpeza, que eu mesmo usei com bastante frequência e é tão comum que presumivelmente foi responsável pela estrutura try{} catch {} não disponível em muitas linguagens.O número de verificações e variáveis ​​extras necessárias para realizar a mesma coisa é muito pior do que uma ou duas instruções para fazer o salto e, novamente, a solução da função adicional não é uma solução.Você não pode me dizer que isso é mais gerenciável ou mais legível.

Agora, o espaço de código, o uso da pilha e o tempo de execução podem não ser importantes o suficiente em muitas situações para muitos programadores, mas quando você está em um ambiente incorporado com apenas 2 KB de espaço de código para trabalhar, 50 bytes de instruções extras para evitar uma instrução claramente definida 'goto' é simplesmente ridículo, e esta não é uma situação tão rara como muitos programadores de alto nível acreditam.

A afirmação de que “goto é prejudicial” foi muito útil na transição para uma programação estruturada, mesmo que tenha sido sempre uma generalização excessiva.Neste ponto, todos nós já ouvimos o suficiente para ter cuidado ao usá-lo (como deveríamos).Quando é obviamente a ferramenta certa para o trabalho, não precisamos ter medo dela.

Você pode usá-lo para interromper um loop profundamente aninhado, mas na maioria das vezes seu código pode ser refatorado para ficar mais limpo, sem loops profundamente aninhados.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top