Why is the use of GOTO considered bad?

Asked

Viewed 12,967 times

106

Some languages, such as C, still have the instruction goto.

I have always heard that using it is not a good practice. I would like to know:

  • What would be the reasons to avoid the goto and what problems are caused by its use?

  • goto still exists for compatibility, or for very restricted use cases?

  • In my view, one of the main reasons is that using GOTO too often confuses the code. If you take a look at the discussions on this subject in the English OS, maybe this will help you understand. http://stackoverflow.com/questions/11906056/goto-is-this-bad http://stackoverflow.com/questions/46586/goto-still-considered-harmful

  • 2

    I’m just waiting for a question about the usefulness of COMEFROM.

  • 2

    Four answers so far and no one has quoted Dijkstra and his famous "Go To Statement Considered Harmful"?

  • 1

    In my case, I will answer the reason why I do not quote him: I do not want my answer to look like "an important guy spoke then is spoken". It is important to understand on your own.

  • 3

    Moreover, contradicting me and now using a quote, just for humor, "Arrogance in computer science is measured in nano-Dijkstras" - Alan Kay :-)

  • 2

    The goal of citing it is precisely with a critical stance, as @bigown did. Do not accept because "an important guy quoted" but include the quote for historical value.

  • 3

    We are in 1980 ?! It has been more than proven that it is a bad programming practice , modern languages no longer have the syntax ... I respect your right to debate but frankly regret the subject.

Show 2 more comments

5 answers

94


Direct response

The reason why people say to avoid the goto is readability. It would be to facilitate the understanding of the code, so the programmer does not get lost in what the code does. The goto in itself does not cause any problem.

The goto still exists because it is still useful. It exists because most language creators are pragmatic.

Where the myth came from?

Basically, the "guilty" is the article "go to statement considered Harmful" by Edsger W. Dijkstra. More precisely from his editor, who chose a very flashy title. Then all the people who talk about what they don’t know perpetuated the myth.

Of course the article shows a situation where the goto does not need to be used. But it does not say that the goto should not exist. In fact the article only tries to sell the idea of structured programming.

In the past programs were understood a lot by their flow, be they in Assembly, or in the first high-level languages. Programming structured and modularized emerged shortly after to revolutionize programming once again. This way the programs would be understood by their structure and not by their flow. This really proved to be a huge facilitator for understanding the programs, which were getting bigger and bigger.

Like the cordless phone used to be people’s favorite joke they started to repeat that the goto fried his brain or made the computer pop bubbles.

What is a Goto?

By the way people talk it seems that word/expression goto is the problem. A break or continue can be considered goto? And a break label? A return? And what will you say to throw?

The throw seems to be one of the most beloved commands of programmers nowadays in most languages. And it can be considered the worst kind of goto. It is difficult to create a control structure as unpredictable as the throw, but I don’t see people speaking ill of it. In fact the treatment of exceptions seems to be one of the most abused resources in programming this century. It’s okay that the abuse occurs more because of the catch where nothing useful can be done, but the catch is just the label of the deviation caused by goto. I talk about it in several posts but it seems that the people who most need to read these things only have time to post their little code problems. See It is good practice to make an exception in such cases?, best way to deal with exceptions and .

Although I find radicalism that seeks problems rather than solutions, I find it more coherent who thinks that any form of goto is bad. Prejudice with the word is that we can not accept.

Of course some implementations of goto, Especially the older ones, they may not be ideal, let you do some crazy stuff that doesn’t help anyone. But of course it is not the problem of the concept, but of the implementation.

He shouldn’t exist?

while x < 10
    print x
    x++
end

Is the same as

while: if x >= 10 goto end
    print x
    x++
    goto while
end:

Of course, the first way reduces the size of the code by having a syntax sugar. But it’s harder to follow the second example than the first?

The goto by itself does not cause problem or even legibility. The misuse of it can cause difficulties to maintain the program.

It might look like I’m advocating the use of goto. Far from it, you should try to avoid its use to the maximum, but not at any cost. The programming based on goto certainly it is quite problematic, but the parsimonious use of the resource by those who understand its functioning and know the difficulties it can impose on their program is not problematic.

It is ironic to see legibility advocates provide unreadable codes just to meet the "rule" that a goto should not be used. There are those who think that flags are better than wearing a goto. It may be in some case, but not for everything. Saving and controlling a variable’s state to avoid the goto is a typical case of increasing complexity in the name of the rule.

There are cases that the goto facilitates readability, the main ones have already been demonstrated in the other answers. A very useful case is to avoid the use of multiple exit points (return) as demonstrated in a reply of mine.

I learned to code using a language that basically only had goto. I used for many years a language that the closest to the goto was just the break and continue. Today I use languages that do not encourage their use but allow limited use. This is the point. I can’t remember the last time I used the infamous one, but I like to know I can use it when I need it.

I hate languages that want to protect the programmer by not allowing to do what can be useful. You just force the use of more complex structures that will be well done, as far as possible, by good programmers, and will be very badly done by other programmers. The absence of goto in the language limits good programmers and does not solve any real problems.

  • Languages that do not have goto complicate the lives of those who need to create code generators for these target languages.
  • Not having goto promotes non-standard complicated constructions, especially in the creation of state machines.
  • Can reduce the ease for really needed optimizations on codes that need to boot maximum performance.
  • The lack of goto increases cyclomatic complexity in many cases.

It’s a shame that a language like Javascript, which is so used as target of other, don’t have a goto native.

Some languages correctly limit or may limit what the goto can do:

  • Certainly the goto should not skip important parts of the code;
  • should not be allowed to enter within a scope forcibly;
  • some people think I shouldn’t allow the flow to go back.

The goto can be used in a structured way. Donald Knuth knows this. Who am I to contest it. It is not the use of goto opposed to structured programming. Abuse of it yes.

Another that gives good tips on how to use the goto properly is Stece Mcconnel at Code Complete.

Perhaps the most complete explanation, not necessarily the best, on the subject is in that article.

No one is obliged to agree with everything I or these people have written. I do not agree with everything. But there are facts and good observations.

Avoid the goto can be as damaging as using it. Overall a goto is worth more than violating the DRY.

What language are we talking about?

AP cites the language C. This is a language that needs a lot of goto to clean resources, release memory, close operations. Someone may complain that this is a language problem. But he’s still useful and that’s what matters.

There are languages that have no form of goto and then they did. Because they saw usefulness.

Every language has a need. Some need more than others, but all can benefit from it to a greater or lesser degree.

Without knowing what language we are talking about, we can only say that the goto is useful yes. Knowing the language, we can say more precisely where it is useful.

I am not going to talk about languages already spoken in other responses, and obviously I am not going to talk about languages that I do not understand.

Legitimate uses in C#

According to the Microsoft reference for the command, some legitimate uses are:

class SwitchTest {
    static void Main() {
        Console.WriteLine("Coffee sizes: 1=Small 2=Medium 3=Large");
        Console.Write("Please enter your selection: ");
        string s = Console.ReadLine();
        int n = int.Parse(s);
        int cost = 0;
        switch (n) {
            case 1:
                cost += 25;
                break;
            case 2:
                cost += 25;
                goto case 1;
            case 3:
                cost += 50;
                goto case 1;
            default:
                Console.WriteLine("Invalid selection.");
                break;
        }
        if (cost != 0) Console.WriteLine("Please insert {0} cents.", cost);
        Console.ReadKey();
    }
}

A common case in C# is to put the goto case <próximo caso>. He is a form of Fall through, since C# does not have this automatic feature, i.e., a block case needs to end with break or goto. Some people think C# should have the Fall through automatic to reduce a code size. Others say this causes bugs.

Another way:

public class GotoTest1 {
    static void Main() {
        int x = 200, y = 4;
        int count = 0;
        string[,] array = new string[x, y];
        for (int i = 0; i < x; i++)
            for (int j = 0; j < y; j++)
                array[i, j] = (++count).ToString();
        Console.Write("Enter the number to search for: ");
        string myNumber = Console.ReadLine();
        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                if (array[i, j].Equals(myNumber)) goto Found;
            }
        }
        Console.WriteLine("The number {0} was not found.", myNumber);
        goto Finish;
    Found:
        Console.WriteLine("The number {0} is found.", myNumber);
    Finish:
        Console.WriteLine("End of search.");
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

In some languages that do not have the reserved word goto this can be obtained by break label. But only because you don’t have the floor goto it ceases to be problem? Skip more than one level of a loop nested is something that command helps. Attempts to avoid it, in general, lead to worse code.

It is likely that language would be less criticized if it created a break label to solve these two cases. This shows that hatred of the resource is irrational. In any case, other ways would still be prohibited and is an undesirable limitation.

A desirable and existing limitation in language is the prohibition of label be in a more internal scope than goto or in another scope. There’s no way to get inside a loop through the goto, for example. Getting out of it is no problem, because it is the goto which is more internal, and not the label. This is not possible:

var a = 1;
var s = "";
if (a == 1) {
    s = "1";
    goto label1;
} else if (a >= 2) {
    s = "2";
    goto label1;
} else {
    label1:
    s += "3";
}

Obviously exit a method with goto is not allowed.

A label can’t be one last statement (yes, the label is a statement) of a method. You need to have something after it even if it’s just a return or a statement emptiness (;).

Within a finally there can be no goto.

The finally will be executed even if a goto try to avoid it. The finally has priority and the * label* of a goto will only receive the program flow when all finallys involved are executed.

try {
    ...
    goto label1;
} finally {
    CloseAll();
}
label1:
    FazAlgo();

In this case the CloseAll() will be executed before the FazAlgo() undoubtedly.

A example found in Code Review where the answer you accept is worse than the one you use goto:

string errorMessage;
while(!TryTableFill(myDataTable, out errorMessage)) {
    DialogResult result = MessageBox.Show("Can't load data:\n" + errorMessage, "Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
    if(result != DialogResult.Retry)
        break;
}

private boolean TryTableFill(myDataTable, out string errorMessage) {
    errorMessage = string.Empty;
    try {
       oleDbDataAdapter1.Fill(myDataTable);
       return true;   
    } catch (Exception ex) {    
       errorMessage = ex.Message;
       return false;
    }       
}

Apparently (not for me) it’s better than:

start:
try {
    oleDbDataAdapter1.Fill(myDataTable);
} catch (Exception ex) {
    DialogResult res = MessageBox.Show("Can't load data:\n" + ex.Message, "Error",
        MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
    if (res == DialogResult.Retry)
        goto start;
}

Swears to avoid the goto in the code below left the code more readable?

var tryAgain = true;
while (tryAgain) {
    try {
        ...
        tryAgain = false;
    } catch (...) {
        tryAgain = true
    }
}

With goto:

tryAgain:
    try {
        ...
    } catch (...) {
        goto tryAgain;
    }

I’ve seen cases where the programmer put a loop and at least 2 unconditional deviations (any combination of break and continue) to avoid a simple, innocent and readable goto.

Example of misuse

var retVal = false;
if(cond1)
    goto exit;
if(cond2)
    goto exit;
if(cond3)
    goto exit;
retVal = true;
exit:
return retVal;

May be replaced by:

return !(cond1 || cond2 || cond3);

Try to use the goto to express its intention and not as a mechanism. In this case the intention did not need to goto nor of a complex structure. The problem here is neither the goto. See the version of it without the use of the devil command and that remains bad:

if(cond1)
    return true;
if(cond2)
    return true;
if(cond3)
    return true;
return false;

Completion

Bad programmers will produce bad code, no matter if you use it goto or not. It is unfortunate to see cases of goto where there is a better construction. But it’s even worse to see bad code because a blind rule was used.

The goto can be useful for:

  • Make code easier to read.
  • Reduce the code size.
  • Avoid repetition of code.
  • Facilitate automatically generated code.
  • Get out of loops nested.
  • Manipulate state machines.
  • Ensure a clean exit from a routine.

Diet tells you to eat less and with more quality to take care of your health, does not tell you not to eat. Take care of your health and use a little, a little bit of goto, whenever he benefits more than hinder.

I put in the Github for future reference.

One more example in question here.

  • 1

    Exceptions also cause a lot of discussion. Just look at the discussions about the Go language, which prefers to return error codes, although it also has something like exceptions to the names of Panic, Recover, and Defer.

  • 1

    @Precisely because pragmatism pulls by utility and not by purisms or concepts invented by some that do not make sense.

  • So some languages are not pragmatic, they say you can’t use, even if you have a legitimate use. You have to see the code to be able to say, I know a lot of people who never had to use goto and their codes are far more complicated than they should be and they can’t even see it.

  • Thanks. I can’t always do it that well, but I always try to do the best I can. The best compliment I got here was that I’m not reductionist, I’m a little bit, but less than people usually are, which ends up creating a problem because programming is not about following recipes. And to show how to do and not only give the recipe requires more complete answers. Not everyone likes this. There are those who complain about my answers. Perhaps because they prefer that others do not evolve on their own, that they always need to be dependent on others.

27

inserir a descrição da imagem aqui Original image: xkcd 292

It is rare to find situations where you need to use it, but I would not completely discard its use.

Although the use of goto almost always is bad programming practice (sure you can find a better way), there are times when it really isn’t a bad choice. Some may even argue that when it’s useful, it’s the best choice.

Consider the following example, in Delphi.

uses
  SysUtils, Windows;

Var
 Nomes : array[0..6] of string = ('João', 'Maria', 'Ciclano', 'Marialva', 'foo', 'bar', 'bazz');
 Indice: Integer; 
Const
 meuNome = 'Ciclano'; 

label encontrouValor;
 
 Begin
 for Indice := 0 to Length(Nomes) -1 do begin   
   if (Nomes[Indice] = meuNome) then
     goto encontrouValor;   
 end;

encontrouValor:
 MessageBox(0, pchar(Format('%s está na posição %d', [Nomes[Indice], indice])), '', MB_OK);
end.

If the content of the constant meuNome exist in the array Nomes, the message will appear as expected, but what if it does not exist? Well, in the example above the result will be something similar to this:

inserir a descrição da imagem aqui

In this example, the use of goto is a bad choice, but what if we establish a condition if...else?

Var
 Nomes : array[0..6] of string = ('João', 'Maria', 'Ciclano', 'Marialva', 'foo', 'bar', 'bazz');
 Indice: Integer;

Const
 meuNome = 'Maria';

 label encontrouValor, naoEncontrou;
 
Begin
 for Indice := 0 to Length(Nomes) -1 do begin 
 MessageBox(0, pchar(Nomes[indice]), '', 0);  
   if (Nomes[Indice] = meuNome) then 
     goto encontrouValor
   else
     goto naoEncontrou
 end;

encontrouValor:
MessageBox(0, pchar(Format('%s está na posição %d', [Nomes[Indice], indice])), '', MB_OK);

naoEncontrou:
MessageBox(0, pchar('O seu nome não existe na lista'), '', MB_OK);
end.

We would again fail, in which case the first name of the list would be displayed in the message(erroneously) and then the message saying that the name does not exist in the list. A more elegant way of this example without using the goto would be something like that:

Var
 Nomes : array[0..6] of string = ('João', 'Maria', 'Ciclano', 'Marialva', 'foo', 'bar', 'bazz');

function fooBar(meuNome: string; var Posicao: integer): Boolean;
Var
 Indice: Integer;
begin
Result := False;

for Indice := 0 to Length(Nomes) -1 do begin 
    if (Nomes[Indice] = meuNome) then begin
       Posicao := Indice;
       Result  := True;     
    end;
end;  
end;

Var
 Pos: Integer;
 Nome: string = 'Maria';
Begin
if fooBar(Nome, Pos) then
   MessageBox(0, pchar(Format('%s está na posição %d na lista', [Nome, Pos])), '', MB_OK)
else
   MessageBox(0, pchar(format('%s não existe na lista', [Nome])), '', MB_OK or MB_ICONERROR);   
 
Readln; 
end.

Situations where the use of goto may be considerable:

Loop nestled: Consider that in a function there are multiple loops(while, for, repeat, etc) and need to get out of all loops the goto can come in handy in situations like this.

Var
 IndiceI, IndiceII, IndiceIII: Integer;

label exitLoop; 

Begin
for IndiceI := 0 to 100 do begin 
    for IndiceII := 0 to 100 do begin
        for IndiceIII := 0 to 100 do begin 
          if(IndiceII = 73) then
             goto exitLoop;
        end;
    end;
end;

exitLoop:
 Writeln('Fora do loop');
 Readln;
end.

Resource clearing: Consider that in a given function you need to allocate some resources, and there is a need to check some conditions, such as the result of another function or the parameters passed to our function, consider the example below.

procedure foo;
Var
 FS: TFileStream;
 HI: HINTERNET;
 SE: TShellExecuteInfo;

label exitFuncao;
begin
{
  Alocação dos objetos:
     fs := TFileStream.Create('C:\foo\bar.baz');
     hi := InternetConnect();
     SE := SE.cbSize := SizeOf(SE);
}
if (GetLastError = ERROR_INTERNET_INVALID_URL) then begin
  FreeAndNil(FS);
  FreeAndNil(HI);
  FreeAndNil(SE);
end else if (GetLastError = ERROR_INTERNET_PROTOCOL_NOT_FOUND) then begin
  FreeAndNil(FS);
  FreeAndNil(HI);
  FreeAndNil(SE);
end else if (GetLastError = ERROR_FILE_CORRUPT) then begin
  FreeAndNil(FS);
  FreeAndNil(HI);
  FreeAndNil(SE);
end else if {.....}
end;
end; 

Wouldn’t it be easier to simplify the cleaning of resources once? The use of goto would do very well in that situation, where you need to leave at various points of the function. See:

procedure foo;
Var
 FS: TFileStream;
 HI: HINTERNET;
 SE: TShellExecuteInfo;

label exitFuncao;
begin
{
  Alocação dos objetos:
     fs := TFileStream.Create('C:\foo\bar.baz');
     hi := InternetConnect();
     SE := SE.cbSize := SizeOf(SE);
}
if (GetLastError = ERROR_INTERNET_INVALID_URL) then goto exitFuncao
 else if (GetLastError = ERROR_INTERNET_PROTOCOL_NOT_FOUND) then goto exitFuncao
  else if (GetLastError = ERROR_FILE_CORRUPT) then goto exitFuncao;
   {else if ...}
    {else if ....}
     {else if .....}

exitFuncao: begin
  FreeAndNil(FS);
  FreeAndNil(HI);
  FreeAndNil(SE);
end;
end;
  • Remembering that Delphi has try .. finally, then the drop would not be necessary in this case. Therefore I prefer to mention C as an example. Unless, of course, we are talking about "Pascal pre-exceptions", in this case only the same drop remains.

18

goto INICIO;
FIM:

In conclusion: If you can write without using Otos, prefer. But if you do that and notice that the code has gotten bigger and worse, don’t be afraid to go back.

goto SAIR;
MEIO:

The classic example:

int do_some_operation() {
    struct something1 smt1;
    struct something2 smt2;
    struct something3 smt3;
    int result;

    if (initialize_something1(&smt1) == FAILURE)
        return FAILURE;

    if (initialize_something2(&smt2) == FAILURE) {
        delete_something1(&smt1);
        return FAILURE;
    }

    if (initialize_something3(&smt3) == FAILURE) {
        delete_something2(&smt2);
        delete_something1(&smt1);
        return FAILURE;
    }

    result = do_work(&smt1, &smt2, &smt3);

    delete_something3(&smt3);
    delete_something2(&smt2);
    delete_something1(&smt1);
    return result;
}

Here we have 3 resources, totaling 4 function output points. At each point we need to clean the resources differently. Now consider that we have more resources, more operations that may fail, arguments, etc.

Notice how it’s best to write like this:

int do_some_operation() {
    struct something1 smt1;
    struct something2 smt2;
    struct something3 smt3;
    int result;

    if (initialize_something1(&smt1) == FAILURE) { result = FAILURE; goto finish0; }
    if (initialize_something2(&smt2) == FAILURE) { result = FAILURE; goto finish1; }
    if (initialize_something3(&smt3) == FAILURE) { result = FAILURE; goto finish2; }

    result = do_work(&smt1, &smt2, &smt3);

finish3: delete_something3(&smt3);
finish2: delete_something2(&smt2);
finish1: delete_something1(&smt1);
finish0:
    return result;
}

The general agreement is that you should look for other ways to write the code and avoid using goto. But you have to be careful not to merely avoid Photos at all costs. Maybe they are the best way to solve your specific case.

One more useful example:

On linux, when using system calls, it can happen that while running on the system the process receives a signal. In this case the call is stopped immediately and the signal is processed. At the end, it returns in the error EINTR which merely indicates that you should call the function again.

restart:
    if (system_call(arg1, &arg2) == -1) {
        if (errno == EINTR) goto restart;
        if (errno == OTHERERROR) ...;
    }

Writing it otherwise would require a loop and would not be very readable.

goto FIM;
INICIO:

In higher-level, object-oriented languages, it doesn’t really make sense to use GOTO. They break the reading flow and are not necessary to clean up since it is quite acceptable that each resource should have an owner and must be destroyed when its owner is destroyed (by its respective owner or by some garbage collection mechanism).

However, when you level down to Assembly, note that there are no loops, conditionals, or even functions. Everything is done with Gotos. The C language was created with performance in mind. So it makes sense that it is possible to work the same way as in Assembly. As here you do not have a clear division of objects, it is a little more complicated to determine the right time to do the cleaning.

goto MEIO;
SAIR:

Did you find the organization of the answer bad? Don’t leave your program like this.

  • 3

    Who knows the organization of the answer turned bad, but only for those who have no notion of the drop and read your answer without paying attention, I found it as normal as the others :D

17

Prior to structured languages, there were languages that contained basically 2 decision/control structures: if and goto. In addition, they could have the maximum function feature and loop for, and that was the end of it. I’m thinking of languages like the early Fortrans and Basics, and there must be many others that have been forgotten in history.

Ah, yes: depending on the language if could only contain 1 instruction, there was no else and the line labels were always numbers. It was basically everything with if and goto. Any abstract block of the type "execute these 10 instructions until such condition occurs" was in the head of the original programmer, because to read the code it was necessary to follow line by line where the droplet would jump. The "blocks" were not evident by the structuring of the code.

Therefore it was necessary to have all the code of the program in the head, since from anywhere could jump to anywhere else, change any variable and go back to anywhere else. It must have been really hard to read.

More modern languages allow you to better structure the code: do/while/for loops, if with Else, functions that receive parameters, return values and can have their own local variables for intermediate calculations.

Then it became clear that it is much easier to program in a structured language, because it better expresses the intention of the programmer and allows read and understand smaller snippets of code before leaving for the next section. That’s why the goto got a bad name, as a symbol of unstructured languages and confusing code.

Another problem is that the droplet can be somewhat tempting for beginners: it may seem easier at first to send a droplet anywhere than to structure a loop with an exit condition at the beginning or end of the block. Even the first implementations of Pascal had neither break nor continue, but after that time most realized that also did not need to get to such a point (because some loops are very readable with the test in the middle followed by break) and many languages today have break, continue as "restricted Gotos" and the case of goto generic is at the discretion of the language designer.

In conclusion: it’s important to learn how to use all the other structures well: while, do/while, for, break, continue, functions (imagine how horrible it would look to simulate functions just with gotos), and for those cases where nothing else serves, there is no problem in using a drop here or there, always in the latter case. In the C language, it is usually the situation of "everything went wrong, quit all the loops, release the memory and finish the function", while in this situation another language could recommend using named breaks or exceptions. Every language has its peculiarity.

  • Bless the high-level languages. Phew!

15

Why the use of GOTO is considered bad?

The drop has unique capabilities to render a code unreadable.

What would be the reasons to avoid GOTO and what problems caused by its use?

I will list below which programming language resources have the potential to make code unreadable:

  • All.

And one of the features that I’ve seen best performing this function of making the code illegible is not GOTO but the nice IF, for example in nested IF codes:

if (...) then
    // ...
else if (...) then
    // ... 
        if (...) then
            // ... 
                if (...) then
                    // ... 
        else
            // ...

But while all language resources can be misused, GOTO, when misused, has special damage-causing powers:

  • GOTO is the only one that has no clear delimitation of beginning and end, ({ }), i.e., GOTO can be used to write blocks of code but can also be used to write recursive code and loops difficult to identify as such.

  • GOTO is the only one who can send you inside a loop in a difficult-to-predict context. Yeah, GOTO can’t send you into formal looping structures (for, while), but if GOTO itself can produce loops and a GOTO can send you inside any label, then GOTO can send the code execution point inside loops.

  • GOTO is the only one that has no variable scope. The other code blocks have their own variable scope, while with GOTO I can skip to after the declaration of a variable and assign and increment its value there, from there I do another GOTO for before the variable declaration and when the execution finally arrives on the line of your statement and first value assignment (int i = 1), what, by the order of the lines and by the command being read seems to be the first use of the variable, is actually overwriting a previously set value.

  • GOTO is the only one that allows an arbitrary flow to the code (labelA -> labelB -> labelC -> back to labelB -> goes nowhere). The other structures (loops, ifs, methods) have an input point and an end marker, when the execution then returns to the caller. A GOTO sends you to another line that may or may not result later in executing the next line, and you will only know the condition for the next line to be executed if you study each line subsequent to the label to which GOTO sent you (These lines, in turn, can send you to other Abels).

For these special features GOTO has more powerful abilities to make code difficult to understand.

The reason GOTO still exists would be for compatibility reasons or a very narrow use case?

The answer may be another question:

If all language features have the potential to make the code unreadable, why kill GOTO?

Someone who has learned to make cool code with GOTO in other languages may want to do it in C as well#.

Examples of good use of GOTO in a modern structured language

I haven’t found that yet. Even more if object orientation is supported, using GOTO seems very unnecessary and there are always more expressive options without it.

For your assessment and impartial evaluation, I will take as an example the codes of microsoft documentation (the same ones used in @Maniero’s reply) and see how they would look without GOTO:

Transfer control to one label specific in a switch-case

The Microsoft example, using GOTO looks like this:

class SwitchTest {
    static void Main() {
        Console.WriteLine("Coffee sizes: 1=Small 2=Medium 3=Large");
        Console.Write("Please enter your selection: ");
        string s = Console.ReadLine();
        int n = int.Parse(s);
        int cost = 0;
        switch (n) {
            case 1:
                cost += 25;
                break;
            case 2:
                cost += 25;
                goto case 1;
            case 3:
                cost += 50;
                goto case 1;
            default:
                Console.WriteLine("Invalid selection.");
                break;
        }
        if (cost != 0) Console.WriteLine("Please insert {0} cents.", cost);
        Console.ReadKey();
    }
}

Let’s see what this code looks like without GOTO:

class SwitchTest {
    static void Main() {
        const int priceSmall = 25;
        const int priceMedium = priceSmall + 25;
        const int priceLarge = priceSmall + 50;
        Console.WriteLine("Coffee sizes: 1=Small 2=Medium 3=Large");
        Console.Write("Please enter your selection: ");
        string s = Console.ReadLine();
        int n = int.Parse(s);
        int cost = 0;
        switch (n)
        {
            case 1:
                cost = priceSmall;
                break;
            case 2:
                cost = priceMedium;
                break;
            case 3:
                cost = priceLarge;
                break;
            default:
                Console.WriteLine("Invalid selection.");
                break;
        }
        if (cost != 0) Console.WriteLine("Please insert {0} cents.", cost);
        Console.ReadKey();
    }
}

Will cost += 50; goto case 1; is more expressive than cost = priceLarge;? The second option seems clearer, simpler and more expressive.

Not to mention that since the language supports object orientation, Enumerators, Generics, polymorphism, methods overload... then there are a thousand other ways to write this code more expressive than using GOTO and even more expressive than using switch-case.

Break deeply nested loops

Microsoft’s second example demonstrates the use of GOTO to break all nested loops at once:

static void Main()
{
    // Procura *myNumber* em um array multidimensional: 
    for (int i = 0; i < x; i++)
    {
        for (int j = 0; j < y; j++)
        {
            if (array[i, j].Equals(myNumber))
            {
                goto Found;
            }
        }
    }
    Console.WriteLine("The number {0} was not found.", myNumber);
    goto Finish;
Found:
    Console.WriteLine("The number {0} is found.", myNumber);
Finish:
    Console.WriteLine("End of search.");
    Console.WriteLine("Press any key to exit.");
    Console.ReadKey();
}

Let’s see what this code looks like without GOTO:

static void Main()
{
    // Procura *myNumber* em um array multidimensional: 
    var Found = false;
    for (int i = 0; i < x && !Found; i++)
    {
        for (int j = 0; j < y && !Found; j++)
        {
            Found = array[i, j].Equals(myNumber);
        }
    }
    Console.WriteLine("The number {0} {1}.", myNumber, Found ? "is found" : "was not found");
    Console.WriteLine("End of search.");
    Console.WriteLine("Press any key to exit.");
    Console.ReadKey();
}

Instead of using GOTO to break the loops, I used a variable. If someone says the code with GOTO is clearer I will not argue, but it is clear to me that without GOTO it has become simpler.

Conclusion on these two Microsoft examples

Just like most Microsoft examples, these two examples with GOTO only seek to demonstrate the potential of a resource and do not indicate a way to use it in real life.

An example where GOTO would seem to save the homeland from the expressive code

Still taking the examples of @Maniero, this rather weird code:

string errorMessage;
while(!TryTableFill(myDataTable, out errorMessage)) {
    DialogResult result = MessageBox.Show("Can't load data:\n" + errorMessage, "Error", 
        MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
    if(result != DialogResult.Retry)
        break;
}

private boolean TryTableFill(myDataTable, out string errorMessage) {
    errorMessage = string.Empty;
    try {
       oleDbDataAdapter1.Fill(myDataTable);
       return true;   
    } catch (Exception ex) {    
       errorMessage = ex.Message;
       return false;
    }       
}

is surely better replaced by this other with GOTO:

start:
try {
    oleDbDataAdapter1.Fill(myDataTable);
} catch (Exception ex) {
    DialogResult res = MessageBox.Show("Can't load data:\n" + ex.Message, "Error",
        MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
    if (res == DialogResult.Retry)
        goto start;
}

In fact, the use of GOTO made the previous code more expressive and simple. But who cares about a more expressive code and does not know that GOTO exists, can also solve like this, without GOTO:

var keepTrying = true;
while (keepTrying) {
    try {
        oleDbDataAdapter1.Fill(myDataTable);
        keepTrying = false;
    } catch (Exception ex) {
        keepTrying = MessageBox.Show("Can't load data:\n" + ex.Message, "Error",
            MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry;
    }
}

At the end the code without GOTO was at least as expressive as the code with GOTO. Deciding which of the two is more expressive seems to me a matter of opinion.

Use of GOTO to implement state machine

If object orientation is available (and in the focus of this answer it is because the examples are all in C#), an excellent example of a state machine, very simple, expressive, and that goes well beyond the GOTO can be found in this OS response: Simple state machine example in C#.

Another answer to that same question makes use of GOTO. Then the author who used GOTO edited the answer by adding a lot of comments in the code "just to clarify", while the answer that doesn’t use GOTO doesn’t depend on any comments and I challenge someone not to understand it :D

Also, the answer that uses object orientation can be easily extended and modified with simple editing of Numerators and a dictionary - it could even be generic and set up by a database! The state machine using GOTO needs to have its own algorithm modified in order to be extended.

Completion

The drop:

  • Not useful for making code easier to read because there are other options that are more readable or at least as readable as.
  • It can be useful to reduce code size but expressive and maintenance-friendly code is more important than short code.
  • It is no more useful to prevent code repetition than encapsulation of code in formal loops, methods and objects.
  • It is no more useful to exit nested loops than to declare a control variable and test its state in the declaration of each loop.
  • No more useful to implement state machine than object orientation.

Behold, programmers who were not born experienced at some point will produce bad code with any tool feature.

These programmers can rather be guided by well-argued good practices to be able to produce good code from an early age and better understand the whys and questioning these whys throughout their career.

Therefore, in order to help these professionals, we sometimes call "bad practice" the practices that have historically shown to bring more harm than good. For example: provide global for state change, capture exceptions without knowing why, micro-optimize code without evidence of need, give insignificant names to artifacts, use inheritance as first option...

Finally, if GOTO is one of these elements with the greatest potential to cause harm and if there are always great options that make it expendable as I have tried to demonstrate in this answer, then using GOTO may be considered bad practice. It is logical that, like any other bad or good practice, this too will always be open to discussions.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.