31

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,854
  • 8
  • 39
  • 46
  • Possible duplicates: [Is it ever worthwhile using goto?](http://programmers.stackexchange.com/questions/566/is-it-ever-worthwhile-using-goto), and [What's the most acceptable use of 'goto' you've ever seen?](http://programmers.stackexchange.com/questions/6755/whats-the-most-acceptable-use-of-goto-youve-ever-seen) – yannis Dec 17 '11 at 13:42
  • "expressive"? What does that even mean, besides "I like it better?" – Sklivvz Dec 17 '11 at 15:23
  • 3
    @sklivvz "expressive" means "someone reading the code can see the intent easily". Yes it's subjective, but it's more meaningful than you suggest. – slim Dec 17 '11 at 16:47
  • @slim I realise that. However, my opinion is that the presented code it's quite the opposite of expressive, and that the code without the `goto` it's perfectly clear. So, while I agree that expressive means something, I also think it's completely abused of, in this case. – Sklivvz Dec 17 '11 at 17:50
  • @sklivvz I agree with you on that. – slim Dec 17 '11 at 17:54
  • example could use some work imho, personally I find both version less expressive than return max(n,0); – jk. Jun 01 '12 at 08:45
  • As an aside, I would consider the `goto` version inferior not because of the `goto`, but because it's (IMO) a poor example of self-documenting code: instead of allowing the code to speak for itself, it inserts additional, extraneous code (the jump & label) for the sole purpose of serving as documentation. I would personally find `if (!(n >= 0))` to be cleaner, if `if (n < 0)` were to be considered insufficient. – Justin Time - Reinstate Monica Jan 22 '19 at 22:39
  • See also https://softwareengineering.stackexchange.com/questions/334417/what-kind-of-bugs-do-goto-statements-lead-to-are-there-any-historically-signi/391321#391321 – Martin Maat Sep 04 '19 at 19:43
  • The assert is of course _not_ free in the goto version. You use assert to find programming errors. You can either say "I never make mistakes" then assert is unneeded in both cases. Or you say "I might make mistakes" and it is needed. Like replacing "if (n >= 0) goto..." with "if (-n < 0) goto..." which will work in all but one cases. – gnasher729 Sep 05 '19 at 19:15

10 Answers10

38

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,137
  • 6
  • 25
  • 33
GrandmasterB
  • 37,990
  • 7
  • 78
  • 131
  • You could say this about _any_ language feature though. "X isn't the problem, the problem is programmers who misuse X." The point is whether or not X encourages misuse. – Jack Sep 05 '19 at 04:23
  • Goto in C++ do have a problem inside template code. I experienced one situation where the goto inside my template class renders the code using it never make any progress (infinite loop). The compiler will COPY these goto into the caller context and cause trouble. – Kemin Zhou Jun 04 '20 at 21:41
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
  • 9,992
  • 6
  • 37
  • 56
  • **Commenters:** Do not continue the bickering back and forth in comments. If you have a better answer or you want to improve your answer, please do so on the question this one duplicates. If you want to have a constructive conversation about the topic of this post, please [do it in chat](http://chat.stackexchange.com/rooms/21). –  Dec 19 '11 at 09:33
  • Amusingly, use of other branch/loop constructs leads to one of the more prominent uses of `goto`: Jumping out of nested conditionals/loops, when there's a viable reason to do so and breaking out without `goto` would look clunky and/or require multiple potentially-costly checks. (I believe the most commonly discussed case of this is error handling, with "specific end condition met in nested loop mid-iteration" being the next most common.) – Justin Time - Reinstate Monica Jul 14 '19 at 19:35
  • @Justin: In C++, I have yet to see such a piece of code that could be made easier to understand by applying a goto, but would not become even easier to understand by introducing additional (inlined) functions. – sbi Sep 04 '19 at 19:10
  • That's a good point, too, I was mainly looking at cases where it's less elegant to e.g., break things apart into discrete functions. Not all that common now, though, to be sure, especially with how much languages have progressed. – Justin Time - Reinstate Monica Sep 05 '19 at 00:10
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.

Péter Török
  • 46,427
  • 16
  • 160
  • 185
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
  • 2
    +1 for use of `goto` as a 'named break,' which is basically the only legitimate use I've seen for it in real life (at least in a world where we have exceptions). – fluffy Dec 17 '11 at 21:06
  • Some languages allow you to use a label together with break. It is however not a well documented thing in e.g. [Java](http://www.roseindia.net/javatutorials/java_break_to_label_tatement.shtml) `break [labelname]` – Spoike Jun 01 '12 at 11:38
  • 1
    This was tagged C++. In C++, you put those loops into a function (which is better for code comprehension anyway), which can be inlined, so that this has no impact at runtime, and **`return`** from that function. – sbi Oct 18 '17 at 08:00
  • Don't simply think about your own code, think also about how developers in the future will maintain your code. It is very easy to add a line between the loop and the label by mistake. I would only allow `goto` for efficiency concerns (e.g. parsers). **The fact that `goto`s are not accepted by most developers also mean they are not used to them, and they WILL do mistakes**. In your case, a sub-function or variable-to-break are much better options. – Adrian Maire Sep 05 '19 at 11:02
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,497
  • 4
  • 25
  • 37
  • 3
    Now, implementing VM's is *hardly* a day-to-day job. // With code generation, no programmer is writing the goto (I'm of the opinion that generated code can follow different rules than "normal" code). // And the last bit about state automatons -- do you have any examples? – Martin Ba Dec 17 '11 at 19:39
  • 1
    @Martin, all the brainwashed goto haters are proposing to kick it off from all the languages they're using. Which means they won't be able to implement a VM properly even when they'd need it. And they won't be able to generate code in their languages too, because they've removed such a useful statement. As for the examples, look at the ADVENT code here: http://www-cs-staff.stanford.edu/~uno/programs.html – SK-logic Dec 18 '11 at 14:40
  • 4
    this question is tagged [C++] and the example you link to is not C++, as far as I am able to tell. Note that I do not "hate goto", as you seem to think of people that think it can and probably should be avoided in most C++ code. I *am* - purely out of curiosity - genuinely interested in a clean C++ code example that does it's job better that a corresponding version without goto. – Martin Ba Dec 19 '11 at 09:23
  • @Martin, mind naming any of the idiomatic C++ features that could have been used here? There is no way C++ could improve the readability of such a basic and trivial thing, so a pure C example is good enough. If you want a nice and clean C++ example - download LLVM source code and grep for `goto`, you'd be surprised (and most of the cases there are FSMs). – SK-logic Dec 19 '11 at 09:34
  • 3
    @SK-logic: this is the most balanced statement on `goto` I have seen in a long time, among all the other crap flying around. Since `goto` is the fundamental construct from which other statements are derived, why deprive the programmer of it's power for the occational need as you outlined. I have personally been in several situations where judicious use of `goto` whipped the pants off of `case, while, if, break, function, throw, finally, etc...` (notably in FSMs). This discussion is like "since you can buy everything premade at Walmart, get rid of all of the real hardware stores." – gahooa Jan 17 '12 at 14:14
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.

OldCurmudgeon
  • 778
  • 5
  • 11
  • 3
    A state automaton implemented with `goto` is much *simpler* than any other, more "structured" implementation. Just as another great man, D. E. Knuth, brilliantly displayed with his adventure game. – SK-logic Dec 17 '11 at 14:24
  • 3
    The state machine is by its very nature predictable to a higher degree than most other code. This is not what Dijkstra was talking about. A Turing machine would be even more predictable, is that therefore the best way to code all algorithms? – OldCurmudgeon Dec 17 '11 at 17:41
  • I did not get your argument - do you mean that state machines are not easy to comprehend and should not be coded directly at all?!? – SK-logic Dec 18 '11 at 14:41
  • 1
    I am saying state machines are a special case as they are almost totally data driven. I believe Dijkstra is discussing algorithms of a more general nature here. I am interested in Knuth's `adventure game`. Can you provide a link? – OldCurmudgeon Dec 18 '11 at 16:18
  • http://www-cs-staff.stanford.edu/~uno/programs.html - see ADVENT there. – SK-logic Dec 18 '11 at 16:26
  • OMG I remember playing that at Uni!!! *"You are in a maze of twisty little passages, all alike."* – OldCurmudgeon Dec 18 '11 at 19:59
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?)

Aaron McDaid
  • 141
  • 3
  • D allows nested functions like that :) – ratchet freak Dec 17 '11 at 15:21
  • "maybe lambdas will allow it" => They already do, as of August 2011. The *current* standard of C++ is C++1x. Also all mainstream compilers (gcc, visual studio, etc) already support lambdas. – Andreas Bonini Dec 17 '11 at 17:04
  • 1
    _GNU C_ (but neither _GNU C++_ nor _GNU Objective C_) allows [nested functions](http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html). Your example code would actually compile and work as expected with GCC. But I would recommend against using this extension. – Mackie Messer Dec 17 '11 at 17:33
  • Everything seen as a lambda? Nice idea, but I don't think C++ is ever going to do this as good as Haskell, which doesn't need to fight with an overall language design that was originally meant to be used in a completely different way. – leftaroundabout Dec 18 '11 at 11:42
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,794
  • 8
  • 70
  • 139
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.

Peter Hofman
  • 101
  • 1
-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,902
  • 11
  • 10
  • 2
    JFYI, `goto` is much older than Basic. Imagine a machine code without branching instruction. Imagine Fortran without a `goto` statement. – SK-logic Dec 17 '11 at 14:25
  • 1
    It's not true that goto's are unnecessary. This is a very broad statement without foundation. – Andreas Bonini Dec 17 '11 at 17:01
  • 1
    @Krelp, maybe in assembly you need goto. In high level languages like C++ or C#, you don't. I've not used goto a single time in the over 10 years I've been programming professionally. I've never encountered a time when it would have been easier or clearer than any alternative. – Andy Dec 17 '11 at 21:38
  • Machine code / assemblers have still the same problem with line numbers. It's just memory addresses, and inserting code in middle of existing code just doesn't work without relocating all the code after it. – tp1 Dec 17 '11 at 23:54
  • @Andy: see http://programmers.stackexchange.com/a/125738/112 for examples of uses where goto is good, along with mine and many others – Andreas Bonini Dec 18 '11 at 00:10
  • @Krelp sorry, not buying it. 99% of developers AREN'T going to implement a VM. Parsing is debatable at best, good OO constructs can handle this. Same with state machine. You've heard of Windows Workflow Foundation I assume. Again, good OO eliminates the need for goto. Can you use it? Ya. Should you? Probably not. – Andy Dec 18 '11 at 01:31
  • 1
    @tp1, there are *labels*. – SK-logic Dec 18 '11 at 14:42
  • @Andy, there is no such a thing as "good OO". OO is almost always a very bad way to express things. And, OO won't eliminate a need for goto. Mind expressing an irreducible loop with OOP? – SK-logic Dec 18 '11 at 14:43
  • @Andy: I don't care if 99% or 10% or 76.54% of programmers don't have a need to use goto. You said that "gotos are unnecessary", this means they always are and for all programmers. I gave you in total 4 valid reasons for their use, I can find more. If you said "gotos are usually unnecessary" I would agree with you. – Andreas Bonini Dec 18 '11 at 18:38
  • @SK-logic ugh, I'm not even going to bother with you. OO is always a bad way to express things? Please. – Andy Dec 18 '11 at 22:49
  • @Krelp please go read my comments again. Stop trying to put words into my mouth to try and prove your point. – Andy Dec 18 '11 at 22:51
  • @Andy, I said "almost always". You're not very good with reading, right? – SK-logic Dec 19 '11 at 07:32
  • @SK-logic Almost always doesn't make your statement any more valid. – Andy Dec 19 '11 at 13:41
  • @Andy, you're a believer, right? You think OOP is a silver bullet? Funny. – SK-logic Dec 19 '11 at 15:17
  • @SK-logic Sorry, I'll get off your lawn now. – Andy Dec 19 '11 at 16:47
  • First misnomer—"C++ is a modern language". The word modern here implies an imagined characteristic that is ultimately meaningless. Age or Modernity is not what makes a technology (e.g. tool) good, excellent, bad, or otherwise.
    Second—I have been using Eiffel solidly for 5+ years, 900K LOC of original code + much more library code, and have never had a need for GOTO. Why? Because it does not exist in the language—at all.
    For me—end of discussion.
    – Liberty Lover Oct 08 '15 at 15:03