Pergunta

Neste tópico, olhamos para exemplos de bons usos de goto em C ou C ++. É inspirado por um resposta que as pessoas votaram até porque eles achavam que eu estava brincando.

Resumo (rótulo mudou de original para fazer a intenção ainda mais claro):

infinite_loop:

    // code goes here

goto infinite_loop;

Por que é melhor do que as alternativas:

  • É de específico. goto é o construção de linguagem, que provoca um desvio incondicional. Alternativas dependem usando estruturas apoiando ramos condicionais, com um degenerado sempre verdadeiro condição.
  • O rótulo documentos a intenção sem comentários adicionais.
  • O leitor não tem que fazer a varredura do código de intervir para os primeiros breaks (Embora ainda é possível para um hackers sem escrúpulos para simular continue com um goto precoce).

Regras:

  • Faça de conta que os gotophobes não ganhar. Entende-se que o anterior não pode ser usado no código real, porque vai contra linguagem estabelecida.
  • Suponha que todos nós já ouvimos de 'Goto considerado nocivo' e saber que Goto pode ser usado para gravação código espaguete.
  • Se você não concordar com um exemplo, criticá-lo por mérito técnico sozinho ( 'Porque as pessoas não gostam Goto' não é uma razão técnica).

Vamos ver se podemos falar sobre isso como adultos.

Editar

Esta questão parece acabado agora. Isso gerou algumas respostas de alta qualidade. Obrigado a todos, especialmente aqueles que tomaram o meu pequeno exemplo de loop a sério. A maioria dos céticos estavam preocupados pela falta de âmbito bloco. Como @quinmars apontou em um comentário, você sempre pode colocar chaves em todo o corpo do ciclo. Noto de passagem que for(;;) e while(true) não dar-lhe as chaves gratuitamente ou (e omitindo-los pode causar erros irritante). De qualquer forma, eu não vou perder mais de seu poder cerebral neste ninharia - Posso viver com o for(;;) e while(true) (tão bem se eu quero manter o meu trabalho) inofensivo e idiomática.

Considerando as outras respostas, vejo que muitas pessoas vêem goto como algo que você sempre ter que reescrever de outra maneira. Claro que você pode evitar uma goto através da introdução de um loop, uma bandeira extra, uma pilha de ifs aninhadas, ou o que quer, mas porque não considerar se goto é talvez a melhor ferramenta para o trabalho? Dito de outra forma, quanto feiúra são pessoas preparadas para suportar a evitar o uso de um built-in recurso de linguagem para a sua finalidade? Minha opinião é que mesmo adicionando uma bandeira é um preço muito alto a pagar. Eu gosto de meus variáveis ??para representar as coisas de os domínios de problemas ou solução. 'Unicamente para evitar uma goto' não cortá-la.

Eu vou aceitar a primeira resposta que deu o padrão C para ramificação de um bloco de limpeza. IMO, isso torna o caso mais forte para um goto de todas as respostas postadas, certamente se você medir pelas contorções um inimigo tem que percorrer para evitá-lo.

Foi útil?

Solução

Aqui está um truque que eu já ouvi de pessoas que utilizam. Eu nunca vi-o no entanto selvagem. E isso só se aplica a C porque C ++ tem RAII para fazer isso mais idiomaticamente.

void foo()
{
    if (!doA())
        goto exit;
    if (!doB())
        goto cleanupA;
    if (!doC())
        goto cleanupB;

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}

Outras dicas

A necessidade clássico para GOTO em C como segue é

for ...
  for ...
    if(breakout_condition) 
      goto final;

final:

Não há nenhuma maneira fácil de sair de loops aninhados sem um goto.

Aqui está o meu exemplo não bobo, (de Stevens APITUE) para chamadas de sistema Unix que pode ser interrompida por um sinal.

restart:
    if (system_call() == -1) {
        if (errno == EINTR) goto restart;

        // handle real errors
    }

A alternativa é um loop degenerada. Esta versão lê como Inglês "se a chamada do sistema foi interrompida por um sinal, reiniciá-lo".

Se o dispositivo de Duff não precisa de um goto, então você também não deveria! ;)

void dsend(int count) {
    int n;
    if (!count) return;
    n = (count + 7) / 8;
    switch (count % 8) {
      case 0: do { puts("case 0");
      case 7:      puts("case 7");
      case 6:      puts("case 6");
      case 5:      puts("case 5");
      case 4:      puts("case 4");
      case 3:      puts("case 3");
      case 2:      puts("case 2");
      case 1:      puts("case 1");
                 } while (--n > 0);
    }
}

código acima da entrada da Wikipedia .

Knuth escreveu um "Programação estruturada com instruções GOTO" de papel, você pode obtê-lo por exemplo de aqui . Você vai encontrar muitos exemplos lá.

Muito comum.

do_stuff(thingy) {
    lock(thingy);

    foo;
    if (foo failed) {
        status = -EFOO;
        goto OUT;
    }

    bar;
    if (bar failed) {
        status = -EBAR;
        goto OUT;
    }

    do_stuff_to(thingy);

OUT:
    unlock(thingy);
    return status;
}

O único caso que eu nunca usar goto é para saltar para a frente, normalmente fora dos blocos, e nunca em blocos. Isso evita abuso de do{}while(0) e outras construções que aumentam assentamento, mantendo código legível, estruturado.

Não tenho nada contra gotos em geral, mas posso pensar em várias razões pelas quais você não gostaria de usá-los para um loop como você mencionou:

  • Não escopo limite, portanto, quaisquer variáveis ??temporários que você utiliza dentro não serão liberados até mais tarde.
  • Não escopo limite, portanto, pode levar a erros.
  • Não faz escopo limite, portanto, você não pode voltar a usar os mesmos nomes de variáveis ??mais tarde no futuro código no mesmo escopo.
  • Não faz escopo limite, portanto, você tem a chance de pular sobre uma declaração de variável.
  • As pessoas não estão acostumados a isso e ele vai fazer seu código mais difícil de ler.
  • loops aninhados deste tipo pode levar a código espaguete, normais laços não vai levar a código espaguete.

Um bom lugar para usar um goto é um procedimento que pode abortar em vários pontos, cada um dos quais exige vários níveis de limpeza. Gotophobes sempre pode substituir os gotos com código estruturado e uma série de testes, mas acho que isso é mais simples, porque elimina o recuo excessivo:

if (!openDataFile())
  goto quit;

if (!getDataFromFile())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!

@ fizzer.myopenid.com: o snippet de código postado é equivalente ao seguinte:

    while (system_call() == -1)
    {
        if (errno != EINTR)
        {
            // handle real errors

            break;
        }
    }

Eu definitivamente preferem esta forma.

Mesmo que eu cresci a odiar esse padrão ao longo do tempo, é in-grained na programação COM.

#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
  HRESULT hr = S_OK;
  IfFailGo( pFoo->PerformAction() );
  IfFailGo( pFoo->SomeOtherAction() );
Error:
  return hr;
}

Aqui está um exemplo de uma boa Goto:

// No Code

Eu vi Goto usado corretamente, mas as situações são normaly feio. É somente quando o uso da própria goto é muito menos pior do que o original. @Johnathon Holland o poblem é que você está versão é menos clara. as pessoas parecem ter medo de variáveis ??locais:

void foo()
{
    bool doAsuccess = doA();
    bool doBsuccess = doAsuccess && doB();
    bool doCsuccess = doBsuccess && doC();

    if (!doCsuccess)
    {
        if (doBsuccess)
            undoB();
        if (doAsuccess)
            undoA();
    }
}

E eu prefiro circuitos como este, mas algumas pessoas preferem while(true).

for (;;)
{
    //code goes here
}

Minha queixa sobre isso é que você perde bloco de escopo; qualquer variáveis ??locais declarada entre os GOTOS continua em vigor se o loop está sempre quebrado de fora. (Talvez você está assumindo a execução do loop para sempre;. Eu não acho que isso é o que o escritor pergunta original foi perguntando, embora)

O problema da delimitação do âmbito é mais um problema com C ++, como alguns objetos podem ser dependendo de sua dtor sendo chamado em momentos apropriados.

Para mim, a melhor razão para usar goto é durante o processo de inicialização multi-etapa onde o que é vital que todos os inits são apoiados fora se um falhar, a la:

if(!foo_init())
  goto bye;

if(!bar_init())
  goto foo_bye;

if(!xyzzy_init())
  goto bar_bye;

return TRUE;

bar_bye:
 bar_terminate();

foo_bye:
  foo_terminate();

bye:
  return FALSE;

Eu não usar goto mim é, no entanto eu fiz trabalho com uma pessoa, uma vez que iria usá-los em casos específicos. Se bem me lembro, sua lógica era em torno de questões de desempenho - ele também tinha regras específicas para como . Sempre na mesma função, eo rótulo foi sempre abaixo da instrução goto.

#include <stdio.h>
#include <string.h>

int main()
{
    char name[64];
    char url[80]; /*The final url name with http://www..com*/
    char *pName;
    int x;

    pName = name;

    INPUT:
    printf("\nWrite the name of a web page (Without www, http, .com) ");
    gets(name);

    for(x=0;x<=(strlen(name));x++)
        if(*(pName+0) == '\0' || *(pName+x) == ' ')
        {
            printf("Name blank or with spaces!");
            getch();
            system("cls");
            goto INPUT;
        }

    strcpy(url,"http://www.");
    strcat(url,name);
    strcat(url,".com");

    printf("%s",url);
    return(0);
}

@ Greg:

Por que não fazer o seu exemplo como este:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top