32

Possible Duplicate:
Is it ever worthwhile using goto?

In a recent article, Andrew Koenig writes:

When asked why goto statements are harmful, most programmers will say something like "because they make programs hard to understand." Press harder, and you may well hear something like "I don't really know, but that's what I was taught." For that reason, I'd like to summarize Dijkstra's arguments.

He then shows two program fragments, one without a goto and and one with a goto:

if (n < 0)
    n = 0;

Assuming that n is a variable of a built-in numeric type, we know that after this code, n is nonnegative.

Suppose we rewrite this fragment:

if (n >= 0) goto nonneg;
n = 0;
nonneg: ;

In theory, this rewrite should have the same effect as the original. However, rewriting has changed something important: It has opened the possibility of transferring control to nonneg from anywhere else in the program.

I emphasized the part that I don't agree with. Modern languages like C++ do not allow goto to transfer control arbitrarily. Here are two examples:

  1. You cannot jump to a label that is defined in a different function.
  2. You cannot jump over a variable initialization.

Now consider composing your code of tiny functions that adhere to the single responsibility principle:

int clamp_to_zero(int n)
{
    if (n >= 0) goto n_is_not_negative:
    n = 0;
n_is_not_negative:
    return n;
}

The classic argument against the goto statement is that control could have transferred from anywhere inside your program to the label n_is_not_negative, but this simply is not (and was never) true in C++. If you try it, you will get a compiler error, because labels are scoped. The rest of the program doesn't even see the name n_is_not_negative, so it's just not possible to jump there. This is a static guarantee!

Now, I'm not saying that this version is better then the one without the goto, but to make the latter as expressive as the first one, we would at least have to insert a comment, or even better yet, an assertion:

int clamp_to_zero(int n)
{
    if (n < 0)
        n = 0;
    // n is not negative at this point
    assert(n >= 0);
    return n;    
}

Note that you basically get the assertion for free in the goto version, because the condition n >= 0 is already written in line 1, and n = 0; satisfies the condition trivially. But that's just a random observation.

It seems to me that "don't use gotos!" is one of those dogmas like "don't use multiple returns!" that stem from a time where the real problem were functions of hundreds or even thousand of lines of code.

So, do we still have a case against the goto statement, other than that it is not particularly useful? I haven't written a goto in at least a decade, but it's not like I was running away in terror whenever I encountered one. 1 Ideally, I would like to see a strong and valid argument against gotos that still holds when you adhere to established programming principles for clean code like the SRP. "You can jump anywhere" is not (and has never been) a valid argument in C++, and somehow I don't like teaching stuff that is not true.

1: Also, I have never been able to resurrect even a single velociraptor, no matter how many gotos I tried :(

fredoverflow
  • 6,954

10 Answers10

37

Gotos are not the problem. They never were. The problem is programmers who use gotos in such a way that it makes code extremely difficult to follow, and possibly places code into an indeterminate state (remember, not every language will be as strict as C++ is). Gotos are just the gun - but the programmer is the one who points it at his foot and pulls the trigger.

In the past I've used gotos on occasions where I concluded it was the best course of action (or, more usually, the least risky course). There's nothing wrong with doing that so long as you are aware of the possible consequences. But new coders don't necessarily have that sense and can make a major mess of things. So I suspect it's far simpler to just teach them never to use gotos.

Glorfindel
  • 3,167
GrandmasterB
  • 39,412
26

Ideally, I would like to see a strong and valid argument against gotos that still holds when you adhere to established programming principles for clean code

goto can be used instead of an if, a switch, for, while, break, return, and many, many other features of the language that all serve different purposes. But there's a reason languages have acquired so many branching and looping constructs: They make it easier to grasp what the code does. Using goto gives up this important feature and leaves you with Assembler-like code to decipher.

A goto is therefore inferior to all those structured language constructs.

I have been following this goto debate for almost two decades now, and in C++ I have yet to see a piece of code that could be made easier to understand by applying a goto, but would not become even easier to understand by applying RAII, introducing additional (inlined) functions, or using one of the above mentioned constructs. (It's different for C, which has no RAII.)

sbi
  • 10,052
19

IMHO

if (n < 0)
  n = 0;

makes it perfectly clear that after that, n >= 0, so a comment explaining this would violate the DRY principle, and an assertion I would consider downright silly or confusing: could the author have faced some strange compiler/optimization bug at this point???

I consider the variant using goto harder to understand than plain if. This of course may simply be the effect of not being used to seeing gotos in code; but then again, most of my (current and future) coworkers aren't either, so they probably feel the same. Thus even if I got used to goto, it would make the code harder to maintain in the long run.

It seems to me that "don't use gotos!" is one of those dogmas like "don't use multiple returns!" that stem from a time where the real problem were functions of hundreds or even thousand of lines of code.

All of the legacy programs I have seen so far contain many, many functions of hundreds (or sometimes even thousands) of lines of code. So I am very happy that at least they don't contain gotos :-) You are right that in a small, clean method goto can't make a big problem; however, if you try to draw a fuzzy line like "you can use goto in functions shorter than n lines", it is inevitably going to be abused by "clever" developers. Not to mention that functions tend to grow over time; what do you do when your originally short and clean function bloats to double its size? Do you remove the gotos then, or refactor? Will your successor a few years down the line remove the gotos, or refactor the code too?... OTOH Dijkstra's rule is clear, and has much less potential to be abused.

Last but not least, your function's name does not express its intent; renaming it to e.g. make_non_negative would leave no doubt about what it does.

YMMV.

14

There is nothing wrong with using goto correctly. For example:

for(int i = 0; i <= 10; ++i)
{
   for(int j = 0; j <= 10; ++j)
   {
      // ..
      if(condition)
        goto end;
      // ..
   }
}

end:

The problem is using goto in a way that makes no sense such as in your example:

if (n >= 0) goto nonneg;
n = 0;
nonneg: ;

I've never ever seen this in real code (and I've seen a lot of nasty stuff) so I don't know what's all the fuss and the hate about.

Using goto that way is equivalent to:

for(int i = 0; n < 0 && i < 1; ++i)
    n = -5;

instead of

if(n < 0)
    n = -5;

This doesn't mean that for is bad, just like goto isn't.

Andreas Bonini
  • 1,073
  • 1
  • 9
  • 16
8

Your examples are not very convincing. There are many other cases when goto is much better than any other option.

One is implementing virtual machines with the computed goto extension in GCC (see OCaml bytecode interpreter for example).

Another (very broad) case is code generation. For example, it is much more efficient (and much easier to comprehend as well) to generate a Packrat parser code from higher level, declarative PEG, using goto to escape from the failing parsing branches. If your language can be used as a target for code generation or if it is a meta language allowing to implement new language features, goto is a must have.

And a third case - state automatons. Here, goto is a natural representation of a concept of moving from one state to another, and therefore a goto-based implementation is much more readable than anything else.

But, unfortunately, many programmers were taught to a nearly religious hate and fear of the goto, and for this reason none of the above arguments are convincing enough for this lot.

SK-logic
  • 8,517
5

Then we must consider the great man's arguments. See a transcription of his original paper for references.

My first remark is that, although the programmer’s activity ends when he has constructed a correct program, the process taking place under control of his program is the true subject matter of his activity, for it is this process that has to effectuate the desired effect, it is this process that in its dynamic behaviour has to satisfy the desired specifications. Yet, once the program has been made, the “making” of the corresponding process is delegated to the machine.

So it is the run-time that is all-important.

My second remark is that our intellectual powers are rather geared to master static relations and that our powers to visualize processes evolving in time are relatively poorly developed. For that reason we should do (as wise programmers aware of our limitations) our utmost best to shorten the conceptual gap between the static program and the dynamic process, to make the correspondence between the program (spread out in text space) and the process (spread out in time) as trivial as possible.

(my emphasis)

More specifically, our aim is to ensure that our conceptual image of the running program matches the actual as far as possible.

And finally

... The unbridled use of the go to statement has as an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress. ...

In my opinion nothing has changed, and n'or will it ever change. goto does make it harder to describe the process progress. And since it has also been proved that any algorithm making use of goto can be rewritten to not use it, my case rests.

Just because you can use goto in a less harmful way that doesn't violate too many rules doesn't mean you should use goto. Dijkstra was right, and still is, you must maximise the simplicity of your code. Simple enough is not acceptable.

4

It has already been said that things such as if and while and for are merely fancy gotos (albeit well-structured and understandable).

But the interesting thing is that many gotos appear where we'd ideally use something like return. @Krelp's answer is a good example:

... start of big function
for(int i = 0; i <= 10; ++i)
{
   for(int j = 0; j <= 10; ++j)
   {
      // ..
      if(condition)
        goto end;
      // ..
   }
}    
end:
... rest of big function

On the very rare occasion I've used goto (temporarily) it where I would have liked to use lightweight nested functions. It would be nice to be able to put these for loops inside a nested function and replace the goto with a return.

void big_function() {
    ... start of big function
    // next few lines declare a nested function
    void nested_function() { // shall be permitted access to the locals in the parent function
        for(int i = 0; i <= 10; ++i)
        {
           for(int j = 0; j <= 10; ++j)
           {
              // ..
              if(condition)
                return;
              // ..
           }
        }    
    }
    nested_function(); // actually call it
    ... rest of big function
}

I'd really like to see this sort of thing in C and C++, maybe lambdas will allow it. In fact, maybe global functions should be deprecated and everything should be seen as a lambda! (PS: Don't some C compilers already allow something like this?)

0

Your example is only true in trivial functions, where I can see the whole thing at once. In longer functions, it's not so trivial.

The if statement guarantees no control freakouts within the scope of one statement. I only need to comprehend that one statement to know that nothing funny happens. The goto statement guarantees no control freakouts within the scope of one function. Therefore, it's inherent that goto is harder to read than if.

DeadMG
  • 36,914
0

I am using coding guidelines which allow goto (in Pascal: GOTO exit;) to drop out of a procedure or function in case of some serious error, or when no further processing is possible for whatever reason. Just to jump to the end of a routine, clean up some of the mess (free memory, unlock resources) and then exit. In fact, when including a procedure or function template in the source to code a new routine, the label 'exit' is declared per default.

About the multiple returns: I am in favour of using a local variable to assign the return value to. This can be done on multiple locations. At the end of the function, only one return is required. Together with goto, this can make code more readable, because not so many nested IF statements are required to get to the end of the routine.

However, there will always be people that tell you not to use it, but those are in most cases the ones that do not need to maintain the code.

-1

There is simple reason why goto's are unnecessary. It's because line numbers are not available any longer. The original reason to add goto statement to basic programming language was the line numbers. The problem was that the line numbers were fixed, and you couldn't change the numbers. Goto was needed when you run out of available line numbers. Fill in all lines from 1-10, and adding new line inside that range required replacing line 7 with goto statement to line 330, moving the old line 7 to line 330, then writing some code to 331-380, and then adding goto from 380 to line 8. Now if you have thousands of blocks like this, the program will look somewhat awful and difficult to follow.

tp1
  • 1,932
  • 11
  • 10