É sempre vantajoso usar ‘goto’ em uma linguagem que suporte loops e funções? Se sim, porque?

Há muito tempo tenho a impressão de que o goto nunca deveria ser usado, se possível. Enquanto lendo libavcodec (que está escrito em C) no outro dia, notei vários usos dele. É sempre vantajoso usar o goto em uma linguagem que suporta loops e funções? Se sim, porque?

Existem algumas razões para usar a declaração “goto” que eu conheço (alguns já falaram com isso):

Limpo, saindo de uma function

Geralmente, em uma function, você pode alocar resources e precisar sair em vários lugares. Os programadores podem simplificar seu código colocando o código de limpeza de resources no final da function, e todos os “pontos de saída” da function iriam para o label de limpeza. Dessa forma, você não precisa escrever código de limpeza em todos os “pontos de saída” da function.

Sair de loops nesteds

Se você está em um loop nested e precisa sair de todos os loops, um goto pode tornar isso muito mais limpo e simples do que quebrar instruções e se-cheques.

Melhorias de desempenho de baixo nível

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

Note que em todos esses exemplos, as gotos são restritas ao escopo de uma única function.

Todo mundo que é anti- goto cita, direta ou indiretamente, o artigo GoTo Considered Harmful de Edsger Dijkstra para substanciar sua posição. Pena que o artigo de Dijkstra não tenha praticamente nada a ver com o modo como as instruções goto são usadas nos dias de hoje e, portanto, o que o artigo diz tem pouca ou nenhuma aplicabilidade na cena moderna de programação. Hoje em dia, o meme sem-imaginação parte de uma religião, até mesmo de suas escrituras ditadas do alto, de seus sumos sacerdotes e do distanciamento (ou pior) dos hereges percebidos.

Vamos colocar o papel de Dijkstra no contexto para lançar um pouco de luz sobre o assunto.

Quando Dijkstra escreveu seu artigo, os idiomas populares da época eram procedimentos processuais não estruturados, como BASIC, FORTRAN (os dialetos anteriores) e várias linguagens assembly. Era bastante comum que pessoas usando linguagens de nível mais alto pulassem por toda a sua base de código em threads de execução contorcidas e torcidas que davam origem ao termo “código de espaguete”. Você pode ver isso pulando para o clássico jogo Trek escrito por Mike Mayfield e tentando descobrir como as coisas funcionam. Tire alguns momentos para analisar isso.

Este é “o uso desenfreado do ir para a declaração” que Dijkstra estava criticando em seu trabalho em 1968. ESTE é o ambiente em que ele viveu que o levou a escrever esse papel. A capacidade de pular em qualquer lugar que você gosta no seu código, em qualquer momento que você gostou, foi o que ele estava criticando e exigindo ser interrompido. Comparar isso com as potências anêmicas de goto em C ou outras linguagens mais modernas é simplesmente risível.

Eu já posso ouvir os cantos elevados dos cultistas enquanto eles enfrentam o herege. “Mas”, eles vão cantar, “você pode tornar o código muito difícil de ler com goto em C.” Oh sim? Você pode tornar o código muito difícil de ler sem goto também. Como este:

 #define _ -F<00||--F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ } 

Não é um goto à vista, então deve ser fácil de ler, certo? Ou que tal este:

 a[900]; b;c;d=1 ;e=1;f; g;h;O; main(k, l)char* *l;{g= atoi(* ++l); for(k= 0;k*k< g;b=k ++>>1) ;for(h= 0;h*h< = g;++h); --h;c=( (h+=g>h *(h+1)) -1)>>1; while(d < =g){ ++O;for (f=0;f< O&&d<=g ;++f)a[ b<<5|c] =d++,b+= e;for( f=0;f 

Não goto lá também. Deve, portanto, ser legível.

Qual é o meu ponto com esses exemplos? Não são os resources de linguagem que tornam o código ilegível e inamovível. Não é a syntax que faz isso. É mau programadores que causam isso. E os programadores ruins, como você pode ver no item acima, podem tornar qualquer recurso de linguagem ilegível e inutilizável. Como os loops for cima. (Você pode vê-los, certo?)

Agora, para ser justo, algumas construções de linguagem são mais fáceis de abusar do que outras. Se você é um programador C, no entanto, eu examinaria mais de perto cerca de 50% dos usos de #define muito antes de eu entrar em uma cruzada contra o goto !

Então, para aqueles que se preocuparam em ler até aqui, há vários pontos importantes a serem observados.

  1. O artigo de Dijkstra sobre declarações goto foi escrito para um ambiente de programação onde o goto era muito mais potencialmente prejudicial do que na maioria das linguagens modernas que não são um montador.
  2. Eliminar automaticamente todos os usos de goto por causa disso é tão racional quanto dizer "Eu tentei me divertir uma vez, mas não gostei, então agora sou contra isso".
  3. Há usos legítimos das declarações goto modernas (anêmicas) em código que não podem ser adequadamente substituídas por outros construtos.
  4. Existem, é claro, usos ilegítimos das mesmas declarações.
  5. Há, também, usos ilegítimos das declarações modernas de controle, como a abominação " godo ", em que um laço sempre-falso é quebrado usando o break no lugar de um goto . Estes são frequentemente piores do que o uso criterioso de goto .

Tenha em mente enquanto você está me votando para baixo com um -1 após o outro que eu usei goto no meu próprio código (não-assembler) precisamente 3 vezes nos últimos 15-20 anos.

Aguardo a enxurrada de gritos indignados e -1 votos com a respiração suspensa.

Obedecer as melhores práticas cegamente não é uma prática recomendada. A idéia de evitar as declarações goto como a principal forma de controle de stream é evitar a produção de código de espaguete ilegível. Se usados ​​com moderação nos lugares certos, às vezes eles podem ser a maneira mais simples e clara de expressar uma ideia. Walter Bright, o criador do compilador C ++ da Zortech e da linguagem de programação D, os usa com frequência, mas de forma criteriosa. Mesmo com as instruções goto , seu código ainda é perfeitamente legível.

Resumindo: Evitar o goto para evitar goto é inútil. O que você realmente quer evitar é produzir código ilegível. Se o seu código de leitura for legível, então não há nada de errado com isso.

Como o goto faz o raciocínio sobre o stream do programa 1 (também conhecido como “código de espaguete”), goto geralmente é usado apenas para compensar os resources ausentes: O uso do goto pode realmente ser aceitável, mas somente se a linguagem não oferecer uma estrutura mais estruturada variante para obter o mesmo objective. Tome o exemplo da dúvida:

A regra com goto que usamos é que goto é bom para avançar para um único ponto de limpeza de saída em uma function.

Isso é verdade – mas somente se o idioma não permitir tratamento de exceção estruturado com código de limpeza (como RAII ou finally ), o que faz o mesmo trabalho melhor (como é especialmente construído para fazê-lo) ou quando há uma boa razão não empregar tratamento estruturado de exceções (mas você nunca terá esse caso, exceto em um nível muito baixo).

Na maioria das outras linguagens, o único uso aceitável do goto é sair de loops nesteds. E mesmo assim é quase sempre melhor levantar o loop externo em um método próprio e usar o return .

Fora isso, goto é um sinal de que não se pensou o suficiente sobre a parte específica do código.


1 Linguagens modernas que suportam goto implementam algumas restrições (por exemplo, goto pode não entrar ou sair de funções) mas o problema fundamentalmente permanece o mesmo.

Aliás, o mesmo também é verdade para outros resources de linguagem, principalmente exceções. E geralmente há regras rígidas em vigor para usar esses resources somente quando indicado, como a regra de não usar exceções para controlar o stream de programa não excepcional.

Bem, há uma coisa que é sempre pior que a de goto's ; uso estranho de outros operadores de stream de programa para evitar um goto:

Exemplos:

  // 1 try{ ... throw NoErrorException; ... } catch (const NoErrorException& noe){ // This is the worst } // 2 do { ...break; ...break; } while (false); // 3 for(int i = 0;...) { bool restartOuter = false; for (int j = 0;...) { if (...) restartOuter = true; if (restartOuter) { i = -1; } } etc etc 

Na instrução switch C # não permite fall-through . Então goto é usado para transferir o controle para um label de caso de switch específico ou o label padrão .

Por exemplo:

 switch(value) { case 0: Console.Writeln("In case 0"); goto case 1; case 1: Console.Writeln("In case 1"); goto case 2; case 2: Console.Writeln("In case 2"); goto default; default: Console.Writeln("In default"); break; } 

Edit: Há uma exceção na regra “sem fall-through”. O fall-through é permitido se uma declaração de caso não tiver código.

#ifdef TONGUE_IN_CHEEK

Perl tem um goto que permite implementar chamadas de homem pobre. 😛

 sub factorial { my ($n, $acc) = (@_, 1); return $acc if $n < 1; @_ = ($n - 1, $acc * $n); goto &factorial; } 

#endif

Ok, então isso não tem nada a ver com o goto do C. Mais seriamente, eu concordo com os outros comentários sobre o uso do goto para limpezas, ou para implementar o dispositivo do Duff , ou algo parecido. É tudo sobre usar, não abusar.

(O mesmo comentário pode ser aplicado a longjmp , exceções, call/cc e semelhantes - eles têm usos legítimos, mas podem ser facilmente abusados. Por exemplo, lançar uma exceção puramente para escaping de uma estrutura de controle profundamente aninhada, sob completamente circunstâncias não excepcionais.)

Eu escrevi mais do que algumas linhas de linguagem assembly ao longo dos anos. Em última análise, todas as linguagens de alto nível se resumem a gotos. Ok, chame-os de “branches” ou “saltos” ou qualquer outra coisa, mas eles são bons. Alguém pode escrever montador sem goto?

Agora com certeza, você pode indicar a um programador Fortran, C ou BASIC que executar um motim com gotos é uma receita para o espaguete à bolonhesa. A resposta, entretanto, não é evitá-los, mas usá-los com cuidado.

Uma faca pode ser usada para preparar comida, libertar alguém ou matar alguém. Nós fazemos sem facas por medo do último? Da mesma forma o goto: usado de forma descuidada dificulta, usado com cuidado ajuda.

Dê uma olhada no When To Use Goto When Programming em C :

Embora o uso de goto seja quase sempre uma má prática de programação (com certeza você pode encontrar uma maneira melhor de fazer XYZ), há momentos em que realmente não é uma má escolha. Alguns podem até argumentar que, quando é útil, é a melhor escolha.

A maior parte do que tenho a dizer sobre o goto realmente se aplica apenas ao C. Se você estiver usando C ++, não há razão para usar o goto no lugar de exceções. Em C, no entanto, você não tem o poder de um mecanismo de tratamento de exceções, portanto, se quiser separar o tratamento de erros do restante da lógica do programa e quiser evitar a reconfiguração do código de limpeza várias vezes em todo o código, então goto pode ser uma boa escolha.

O que eu quero dizer? Você pode ter algum código semelhante a este:

 int big_function() { /* do some work */ if([error]) { /* clean up*/ return [error]; } /* do some more work */ if([error]) { /* clean up*/ return [error]; } /* do some more work */ if([error]) { /* clean up*/ return [error]; } /* do some more work */ if([error]) { /* clean up*/ return [error]; } /* clean up*/ return [success]; } 

Isso é bom até você perceber que precisa alterar o código de limpeza. Então você tem que passar e fazer 4 mudanças. Agora, você pode decidir que você pode apenas encapsular toda a limpeza em uma única function; isso não é uma má ideia. Mas isso significa que você precisa ter cuidado com pointers – se você planeja liberar um ponteiro na sua function de limpeza, não há como configurá-lo para apontar para NULL, a menos que você passe um ponteiro para um ponteiro. Em muitos casos, você não usará esse ponteiro novamente, de modo que isso pode não ser uma grande preocupação. Por outro lado, se você adicionar um novo ponteiro, identificador de arquivo ou outra coisa que precise de limpeza, será necessário alterar sua function de limpeza novamente; e então você precisará alterar os argumentos para essa function.

Usando goto , será

 int big_function() { int ret_val = [success]; /* do some work */ if([error]) { ret_val = [error]; goto end; } /* do some more work */ if([error]) { ret_val = [error]; goto end; } /* do some more work */ if([error]) { ret_val = [error]; goto end; } /* do some more work */ if([error]) { ret_val = [error]; goto end; } end: /* clean up*/ return ret_val; } 

O benefício aqui é que o seu código seguindo o fim tem access a tudo o que será necessário para realizar a limpeza, e você conseguiu reduzir consideravelmente o número de pontos de mudança. Outro benefício é que você passou de vários pontos de saída para sua function para apenas um; não há nenhuma chance de você acidentalmente retornar da function sem limpar.

Além disso, como o goto só está sendo usado para pular para um único ponto, não é como se você estivesse criando uma massa de código de espaguete pulando para frente e para trás na tentativa de simular chamadas de function. Em vez disso, na verdade, goto ajuda a escrever código mais estruturado.


Em uma palavra, goto deve sempre ser usado com moderação e como último recurso – mas há um tempo e um lugar para isso. A questão não deve ser “você tem que usá-lo”, mas “é a melhor escolha” para usá-lo.

Acho engraçado que algumas pessoas vão tão longe a ponto de dar uma lista de casos em que o goto é aceitável, dizendo que todos os outros usos são inaceitáveis. Você realmente acha que conhece todos os casos em que goto é a melhor escolha para expressar um algoritmo?

Para ilustrar, vou dar um exemplo que ninguém aqui mostrou ainda:

Hoje eu estava escrevendo código para inserir um elemento em uma tabela de hash. A tabela de hash é um cache de cálculos anteriores que podem ser sobrescritos à vontade (afetando o desempenho, mas não a correção).

Cada bloco da tabela de hash tem 4 slots e eu tenho vários critérios para decidir qual elemento replace quando um bloco estiver cheio. Neste momento, isso significa fazer até três passes através de um balde, assim:

 // Overwrite an element with same hash key if it exists for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++) if (slot_p[add_index].hash_key == hash_key) goto add; // Otherwise, find first empty element for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++) if ((slot_p[add_index].type == TT_ELEMENT_EMPTY) goto add; // Additional passes go here... add: // element is written to the hash table here 

Agora, se eu não usasse o goto, como seria esse código?

Algo assim:

 // Overwrite an element with same hash key if it exists for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++) if (slot_p[add_index].hash_key == hash_key) break; if (add_index >= ELEMENTS_PER_BUCKET) { // Otherwise, find first empty element for (add_index=0; add_index < ELEMENTS_PER_BUCKET; add_index++) if ((slot_p[add_index].type == TT_ELEMENT_EMPTY) break; if (add_index >= ELEMENTS_PER_BUCKET) // Additional passes go here (nested further)... } // element is written to the hash table here 

Ficaria pior e pior se mais passagens fossem adicionadas, enquanto a versão com goto mantém o mesmo nível de recuo em todos os momentos e evita o uso de instruções espúrias if cujo resultado é implícito pela execução do loop anterior.

Portanto, há outro caso em que o goto torna o código mais limpo e mais fácil de escrever e entender ... Tenho certeza de que existem muitos mais, então não finja saber todos os casos em que goto é útil, disser quaisquer bons que você possa pense em.

A regra com goto que usamos é que goto é bom para avançar para um único ponto de limpeza de saída em uma function. Em funções realmente complexas, relaxamos essa regra para permitir outro salto para frente. Em ambos os casos, estamos evitando instruções if aninhadas profundamente que geralmente ocorrem com a verificação do código de erro, o que ajuda a capacidade de leitura e manutenção.

Uma das razões que goto é ruim, além do estilo de codificação, é que você pode usá-lo para criar loops sobrepostos , mas não nesteds :

 loop1: a loop2: b if(cond1) goto loop1 c if(cond2) goto loop2 

Isso criaria a estrutura de stream de controle bizarra, mas possivelmente legal, em que uma sequência como (a, b, c, b, a, b, a, b, …) é possível, o que torna os hackers de compiladores infelizes. Aparentemente, existem vários truques inteligentes de otimização que não dependem desse tipo de estrutura. (Eu deveria checar minha cópia do livro do dragão …) O resultado disso pode (usando alguns compiladores) ser que outras otimizações não são feitas para o código que contém goto s.

Pode ser útil se você souber apenas, “oh, a propósito”, para persuadir o compilador a emitir código mais rápido. Pessoalmente, eu prefiro tentar explicar ao compilador sobre o que é provável e o que não é antes de usar um truque como o goto, mas, sem dúvida, eu também poderia tentar o goto antes de invadir o montador.

The most thoughtful and thorough discussion of goto statements, their legitimate uses, and alternative constructs that can be used in place of “virtuous goto statements” but can be abused as easily as goto statements, is Donald Knuth’s article ” Structured Programming with goto Statements “, in the December 1974 Computing Surveys (volume 6, no. 4. pp. 261 – 301).

Not surprisingly, some aspects of this 39-year old paper are dated: Orders-of-magnitude increases in processing power make some of Knuth’s performance improvements unnoticeable for moderately sized problems, and new programming-language constructs have been invented since then. (For example, try-catch blocks subsume Zahn’s Construct, although they are rarely used in that way.) But Knuth covers all sides of the argument, and should be required reading before anyone rehashes the issue yet again.

In a Perl module, you occasionally want to create subroutines or closures on the fly. The thing is, that once you have created the subroutine, how do you get to it. You could just call it, but then if the subroutine uses caller() it won’t be as helpful as it could be. That is where the goto &subroutine variation can be helpful.

Here is a quick example:

 sub AUTOLOAD{ my($self) = @_; my $name = $AUTOLOAD; $name =~ s/.*:://; *{$name} = my($sub) = sub{ # the body of the closure } goto $sub; # nothing after the goto will ever be executed. } 

You can also use this form of goto to provide a rudimentary form of tail-call optimization.

 sub factorial($){ my($n,$tally) = (@_,1); return $tally if $n < = 1; $tally *= $n--; @_ = ($n,$tally); goto &factorial; } 

( In Perl 5 version 16 that would be better written as goto __SUB__; )

There is a module that will import a tail modifier and one that will import recur if you don't like using this form of goto .

 use Sub::Call::Tail; sub AUTOLOAD { ... tail &$sub( @_ ); } use Sub::Call::Recur; sub factorial($){ my($n,$tally) = (@_,1); return $tally if $n < = 1; recur( $n-1, $tally * $n ); } 

Most of the other reasons to use goto are better done with other keywords.

Like redo ing a bit of code:

 LABEL: ; ... goto LABEL if $x; 
 { ... redo if $x; } 

Or going to the last of a bit of code from multiple places:

 goto LABEL if $x; ... goto LABEL if $y; ... LABEL: ; 
 { last if $x; ... last if $y ... } 

Se sim, porque?

C has no multi-level/labelled break, and not all control flows can be easily modelled with C’s iteration and decision primitives. gotos go a long way towards redressing these flaws.

Sometimes it’s clearer to use a flag variable of some kind to effect a kind of pseudo-multi-level break, but it’s not always superior to the goto (at least a goto allows one to easily determine where control goes to, unlike a flag variable), and sometimes you simply don’t want to pay the performance price of flags/other contortions to avoid the goto.

libavcodec is a performance-sensitive piece of code. Direct expression of the control flow is probably a priority, because it’ll tend to run better.

Just as well no one ever implemented the “COME FROM” statement….

I find the do{} while(false) usage utterly revolting. It is conceivable might convince me it is necessary in some odd case, but never that it is clean sensible code.

If you must do some such loop, why not make the dependence on the flag variable explicit?

 for (stepfailed=0 ; ! stepfailed ; /*empty*/) 

The GOTO can be used, of course, but there is one more important thing than the code style, or if the code is or not readable that you must have in mind when you use it: the code inside may not be as robust as you think .

For instance, look at the following two code snippets:

 If A <> 0 Then A = 0 EndIf Write("Value of A:" + A) 

An equivalent code with GOTO

 If A == 0 Then GOTO FINAL EndIf A = 0 FINAL: Write("Value of A:" + A) 

The first thing we think is that the result of both bits of code will be that “Value of A: 0” (we suppose an execution without parallelism, of course)

That’s not correct: in the first sample, A will always be 0, but in the second sample (with the GOTO statement) A might not be 0. Why?

The reason is because from another point of the program I can insert a GOTO FINAL without controlling the value of A.

This example is very obvious, but as programs get more complicated, the difficulty of seeing those kind of things increases.

Related material can be found into the famous article from Mr. Dijkstra “A case against the GO TO statement”

1) The most common use of goto that I know of is emulating exception handling in languages that don’t offer it, namely in C. (The code given by Nuclear above is just that.) Look at the Linux source code and you’ll see a bazillion gotos used that way; there were about 100,000 gotos in Linux code according to a quick survey conducted in 2013: http://blog.regehr.org/archives/894 . Goto usage is even mentioned in the Linux coding style guide: https://www.kernel.org/doc/Documentation/CodingStyle . Just like object-oriented programming is emulated using structs populated with function pointers, goto has its place in C programming. So who is right: Dijkstra or Linus (and all Linux kernel coders)? It’s theory vs. practice basically.

There is however the usual gotcha for not having compiler-level support and checks for common constructs/patterns: it’s easier to use them wrong and introduce bugs without compile-time checks. Windows and Visual C++ but in C mode offer exception handling via SEH/VEH for this very reason: exceptions are useful even outside OOP languages, ie in a procedural language. But the compiler can’t always save your bacon, even if it offers syntactic support for exceptions in the language. Consider as example of the latter case the famous Apple SSL “goto fail” bug, which just duplicated one goto with disastrous consequences ( https://www.imperialviolet.org/2014/02/22/applebug.html ):

 if (something()) goto fail; goto fail; // copypasta bug printf("Never reached\n"); fail: // control jumps here 

You can have exactly the same bug using compiler-supported exceptions, eg in C++:

 struct Fail {}; try { if (something()) throw Fail(); throw Fail(); // copypasta bug printf("Never reached\n"); } catch (Fail&) { // control jumps here } 

But both variants of the bug can be avoided if the compiler analyzes and warns you about unreachable code. For example compiling with Visual C++ at the /W4 warning level finds the bug in both cases. Java for instance forbids unreachable code (where it can find it!) for a pretty good reason: it’s likely to be a bug in the average Joe’s code. As long as the goto construct doesn’t allow targets that the compiler can’t easily figure out, like gotos to computed addresses(**), it’s not any harder for the compiler to find unreachable code inside a function with gotos than using Dijkstra-approved code.

(**) Footnote: Gotos to computed line numbers are possible in some versions of Basic, eg GOTO 10*x where x is a variable. Rather confusingly, in Fortran “computed goto” refers to a construct that is equivalent to a switch statement in C. Standard C doesn’t allow computed gotos in the language, but only gotos to statically/syntactically declared labels. GNU C however has an extension to get the address of a label (the unary, prefix && operator) and also allows a goto to a variable of type void*. See https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html for more on this obscure sub-topic. The rest of this post ins’t concerned with that obscure GNU C feature.

Standard C (ie not computed) gotos are not usually the reason why unreachable code can’t be found at compile time. The usual reason is logic code like the following. Dado

 int computation1() { return 1; } int computation2() { return computation1(); } 

It’s just as hard for a compiler to find unreachable code in any of the following 3 constructs:

 void tough1() { if (computation1() != computation2()) printf("Unreachable\n"); } void tough2() { if (computation1() == computation2()) goto out; printf("Unreachable\n"); out:; } struct Out{}; void tough3() { try { if (computation1() == computation2()) throw Out(); printf("Unreachable\n"); } catch (Out&) { } } 

(Excuse my brace-related coding style, but I tried to keep the examples as compact as possible.)

Visual C++ /W4 (even with /Ox) fails to find unreachable code in any of these, and as you probably know the problem of finding unreachable code is undecidable in general. (If you don’t believe me about that: https://www.cl.cam.ac.uk/teaching/2006/OptComp/slides/lecture02.pdf )

As a related issue, the C goto can be used to emulate exceptions only inside the body of a function. The standard C library offers a setjmp() and longjmp() pair of functions for emulating non-local exits/exceptions, but those have some serious drawbacks compared to what other languages offer. The Wikipedia article http://en.wikipedia.org/wiki/Setjmp.h explains fairly well this latter issue. This function pair also works on Windows ( http://msdn.microsoft.com/en-us/library/yz2ez4as.aspx ), but hardly anyone uses them there because SEH/VEH is superior. Even on Unix, I think setjmp and longjmp are very seldom used.

2) I think the second most common use of goto in C is implementing multi-level break or multi-level continue, which is also a fairly uncontroversial use case. Recall that Java doesn’t allow goto label, but allows break label or continue label. According to http://www.oracle.com/technetwork/java/simple-142616.html , this is actually the most common use case of gotos in C (90% they say), but in my subjective experience, system code tends to use gotos for error handling more often. Perhaps in scientific code or where the OS offers exception handling (Windows) then multi-level exits are the dominant use case. They don’t really give any details as to the context of their survey.

Edited to add: it turns out these two use patterns are found in the C book of Kernighan and Ritchie, around page 60 (depending on edition). Another thing of note is that both use cases involve only forward gotos. And it turns out that MISRA C 2012 edition (unlike the 2004 edition) now permits gotos, as long as they are only forward ones.

In Perl, use of a label to “goto” from a loop – using a “last” statement, which is similar to break.

This allows better control over nested loops.

The traditional goto label is supported too, but I’m not sure there are too many instances where this is the only way to achieve what you want – subroutines and loops should suffice for most cases.

The problem with ‘goto’ and the most important argument of the ‘goto-less programming’ movement is, that if you use it too frequently your code, although it might behave correctly, becomes unreadable, unmaintainable, unreviewable etc. In 99.99% of the cases ‘goto’ leads to spaghetti code. Personally, I cannot think of any good reason as to why I would use ‘goto’.

Edsger Dijkstra, a computer scientist that had major contributions on the field, was also famous for criticizing the use of GoTo. There’s a short article about his argument on Wikipedia .

I use goto in the following case: when needed to return from funcions at different places, and before return some uninitialization needs to be done:

non-goto version:

 int doSomething (struct my_complicated_stuff *ctx) { db_conn *conn; RSA *key; char *temp_data; conn = db_connect(); if (ctx->smth->needs_alloc) { temp_data=malloc(ctx->some_size); if (!temp_data) { db_disconnect(conn); return -1; } } ... if (!ctx->smth->needs_to_be_processed) { free(temp_data); db_disconnect(conn); return -2; } pthread_mutex_lock(ctx->mutex); if (ctx->some_other_thing->error) { pthread_mutex_unlock(ctx->mutex); free(temp_data); db_disconnect(conn); return -3; } ... key=rsa_load_key(....); ... if (ctx->something_else->error) { rsa_free(key); pthread_mutex_unlock(ctx->mutex); free(temp_data); db_disconnect(conn); return -4; } if (ctx->something_else->additional_check) { rsa_free(key); pthread_mutex_unlock(ctx->mutex); free(temp_data); db_disconnect(conn); return -5; } pthread_mutex_unlock(ctx->mutex); free(temp_data); db_disconnect(conn); return 0; } 

goto version:

 int doSomething_goto (struct my_complicated_stuff *ctx) { int ret=0; db_conn *conn; RSA *key; char *temp_data; conn = db_connect(); if (ctx->smth->needs_alloc) { temp_data=malloc(ctx->some_size); if (!temp_data) { ret=-1; goto exit_db; } } ... if (!ctx->smth->needs_to_be_processed) { ret=-2; goto exit_freetmp; } pthread_mutex_lock(ctx->mutex); if (ctx->some_other_thing->error) { ret=-3; goto exit; } ... key=rsa_load_key(....); ... if (ctx->something_else->error) { ret=-4; goto exit_freekey; } if (ctx->something_else->additional_check) { ret=-5; goto exit_freekey; } exit_freekey: rsa_free(key); exit: pthread_mutex_unlock(ctx->mutex); exit_freetmp: free(temp_data); exit_db: db_disconnect(conn); return ret; } 

The second version makes it easier, when you need to change something in the deallocation statements (each is used once in the code), and reduces the chance to skip any of them, when adding a new branch. Moving them in a function will not help here, because the deallocation can be done at different “levels”.

Some say there is no reason for goto in C++. Some say that in 99% cases there are better alternatives. To be concrete, here’s an example where goto leads to a nice code, something like enhanced do-while loop:

 int i; again: std::cout < < "insert number: "; std::cin >> i; if(std::cin.fail()) { std::cin.clear(); std::cin.ignore(1000,'\n'); goto again; } std::cout < < "your number is " << i; 

Compare it to goto-free code:

 int i; bool loop; do { loop = false; std::cout < < "insert number: "; std::cin >> i; if(std::cin.fail()) { std::cin.clear(); std::cin.ignore(1000,'\n'); loop = true; } } while(loop); std::cout < < "your number is " << i; 

I see these differences:

  • nested {} block is needed (albeit do {...} while looks more familiar)
  • extra loop variable is needed, used in four places
  • it takes longer time to read and understand the work with the loop
  • the loop does not hold any data, it just controls the flow of the execution, which is less comprehensible than simple label

The point is that goto can be easily misused, but goto itself is not to blame. Note that label has function scope in C++, so it does not pollute global scope like in pure assembly, in which overlapping loops has its place and are very common - like in the following code for 8051, where 7segment display is connected to P1. The program loops lightning segment around:

 ; P1 states loops ; 11111110 < - ; 11111101 | ; 11111011 | ; 11110111 | ; 11101111 | ; 11011111 | ; |_________| again: MOV P1,#11111110b ACALL delay loop: MOV A,P1 RL A MOV P1,A ACALL delay JNB P1.5, again SJMP loop