97

I understand that save for breaking out of loops nested in loops; the goto statement is evaded and reviled as a bug prone style of programming, to never be used.

XKCD Alt Text: "Neal Stephenson thinks it's cute to name his labels 'dengo' "
See the original comic at: http://xkcd.com/292/

Because I learned this early; I don't really have any insight or experience on what types of bugs goto actually leads to. So what are we talking about here:

  • Instability?
  • Unmaintainable or unreadable code?
  • Security vulnerabilities?
  • Something else entirely?

What kind of bugs do "goto" statements actually lead to? Are there any historically significant examples?

  • 29
    Have you read Dijkstra's original short paper on this topic? (That is a Yes/No question, and any answer other than an unequivocal instant "yes" is a "no".) – John R. Strohm Oct 24 '16 at 12:11
  • 2
    I presume an alarm exit is when something catastrophic failure happens. where you will never return from. Like power failure, disappearing devices or files. Some kinds of "on error goto"... But I think most "almost catastropic" errors can be handled by "try - catch" constructs nowadays. – Lenne Oct 24 '16 at 13:26
  • 12
    Nobody's mentioned the calculated GOTO yet. Back when the earth was young BASIC had line numbers. You could write the following: 100 N = A*100; GOTO N. Try debugging that :) – mcottle Oct 24 '16 at 22:56
  • 7
    @JohnR.Strohm I have read Dijkstra's paper. It was written in 1968 and suggests that the need for gotos might be reduced in languages that include such advanced features as switch statements and functions. It was written in response to languages in which goto was the primary method of flow control, and could be used to jump to any point in the program. Whereas in languages that were developed after this paper was written, e.g. C, goto can only jump to places in the same stack frame, and is generally used only when other options don't work well. (continued...) – Ray Oct 25 '16 at 6:22
  • 10
    (...continued) His points were valid at the time, but are no longer relevant, regardless of whether goto is a good idea or not. If we want to argue that one way or another, arguments must be made that relate to modern languages. By the way, have you read Knuth's "Structured Programming with goto Statements"? – Ray Oct 25 '16 at 6:25

10 Answers 10

68

It isn't that the goto is bad by itself. (After all, every jump instruction in a computer is a goto.) The problem is that there is a human style of programming that pre-dates structured programming, what could be called "flow-chart" programming.

In flow-chart programming (which people of my generation learned, and was used for the Apollo moon program) you make a diagram with blocks for statement executions and diamonds for decisions, and you could connect them with lines that go all over the place. (So-called "spaghetti code".)

The problem with spaghetti code is that you, the programmer, could "know" it was right, but how could you prove it, to yourself or anybody else? In fact, it might actually have a potential misbehavior, and your knowledge that it is always correct could be wrong.

Along came structured programming with begin-end blocks, for, while, if-else, and so on. These had the advantage that you could still do anything in them, but if you were at all careful, you could be sure your code was correct.

Of course, people can still write spaghetti code even without goto. The common method is to write a while(...) switch( iState ){..., where different cases set the iState to different values. In fact, in C or C++ one could write a macro to do that, and name it GOTO, so saying you're not using goto is a distinction without a difference.

As an example of how code-proving can preclude unrestricted goto, a long time ago I stumbled on a control structure useful for dynamically-changing user interfaces. I called it differential execution. It is fully Turing-universal, but its correctness proof depends on pure structured programming - no goto, return, continue, break, or exceptions.

enter image description here

  • 48
    Correctness cannot be proven except in the most trivial of programs, even when structured programming is used to write them. You can only increase your level of confidence; structured programming does accomplish that. – Robert Harvey Oct 23 '16 at 22:24
  • 23
    @MikeDunlavey: Related: "Beware of bugs in the above code; I have only proved it correct, not tried it." staff.fnwi.uva.nl/p.vanemdeboas/knuthnote.pdf – Mooing Duck Oct 24 '16 at 0:39
  • 26
    @RobertHarvey It's not quite true that correctness proofs are only practical for trivial programs. More specialized tools than structured programming are necessary, however. – Mike Haskel Oct 24 '16 at 5:09
  • 18
    @MSalters: It's a research project. The goal of such a research project is to show that they could write a verified compiler if they had the resources. If you look at their current work, you will see that they are simply not interested in supporting later versions of C, rather they are interested in expanding the correctness properties they can prove. And for that, it is simply completely irrelevant whether they support C90, C99, C11, Pascal, Fortran, Algol, or Plankalkül. – Jörg W Mittag Oct 24 '16 at 8:06
  • 8
    @martineau: I'm leery of those "boson phrases", where programmers sort of fall in line agreeing that it's bad or good, because they'll get sat on if they don't. There have to be more basic reasons for things. – Mike Dunlavey Oct 24 '16 at 16:31
60

Why is goto dangerous?

  • goto doesn't cause instability by itself. Despite about 100,000 gotos, the Linux kernel is still a model of stability.
  • goto by itself should not cause security vulnerabilities. In some languages however, mixing it with try/catch exception management blocks could lead to vulnerabilities as explained in this CERT recommendation. Mainstream C++ compilers flag and prevent such errors, but unfortunately, older or more exotic compilers don't.
  • goto causes unreadable and unmaintainable code. This is also called spaghetti code, because, like in a spaghetti plate, it's very difficult to follow the flow of control when there are too many gotos.

Even if you manage to avoid spaghetti code and if you use only a few gotos, they still facilitate bugs like and resource leaking:

  • Code using structure programming, with clear nested blocks and loops or switches, is easy to follow; its flow of control is very predictable. It's therefore easier to ensure that invariants are respected.
  • With a goto statement, you break that straightforward flow, and break the expectations. For example, you might not notice that you have still to free resources.
  • Many goto in different places can send you to a single goto target. So it's not obvious to know for sure the state you are in when reaching this place. The risk of making wrong/unfounded assumptions is hence quite big.

Additional information and quotes:

C provides the infinitely-abusable goto statement and labels to branch to. Formally the goto is never necessary, and in practice it is almost always easy to write code without it. (...)
Nonetheless we will suggest a few situations where goto 's may find a place. The most common use is to abandon processing in some deeply nested structures, such as breaking out of two loops at once. (...)
Although we are not dogmatic about the matter, it does seem that goto statements should be used sparingly, if at all.

  • James Gosling & Henry McGilton wrote in their 1995 Java language environment white paper:

    No More Goto Statements
    Java has no goto statement. Studies illustrated that goto is (mis)used more often than not simply “because it’s there”. Eliminating goto led to a simplification of the language (...) Studies on approximately 100,000 lines of C code determined that roughly 90 percent of the goto statements were used purely to obtain the effect of breaking out of nested loops. As mentioned above, multi-level break and continue remove most of the need for goto statements.

  • Bjarne Stroustrup defines goto in his glossary in these inviting terms:

    goto - the infamous goto. Primarily useful in machine generated C++ code.

When could goto be used?

Like K&R I'm not dogmatic about gotos. I admit that there are situations where goto could be ease one's life.

Typically, in C, goto allows multilevel loop exit, or error handling requiring to reach an appropriate exit point that frees/unlocks all the resources that were allocated so far (i.e.multiple allocation in sequence means multiple labels). This article quantifies the different uses of the goto in the Linux kernel.

Personally I prefer to avoid it and in 10 years of C, I used maximum 10 gotos. I prefer to use nested ifs, which I think are more readable. When this would lead to a too deep nesting, I'd opt either to decompose my function in smaller parts, or use an boolean indicator in cascade. Today's optimizing compilers are clever enough to generate almost the same code than the same code with goto.

The use of goto heavily depends on the language:

  • In C++, proper use of RAII causes the compiler to automatically destroy objects that go out of scope, so that the resources/lock will be cleaned anyway, and no real need for goto any more.

  • In Java there's no need for goto (see Java's author quote above and this excellent Stack Overflow answer): the garbage collector that cleans the mess, break,continue, and try/catch exception handling cover all the case where goto could be helpful, but in a safer and better manner. Java's popularity proves that goto statement can be avoided in a modern language.

Zoom on the famous SSL goto fail vulnerability

Important Disclaimer: in view of the fierce discussion in the comments, I want to clarify that I don't pretend that the goto statement is the only cause of this bug. I don't pretend that without goto there would be no bug. I just want to show that a goto can be involved in a serious bug.

I don't know how many serious bugs are related to goto in the history of programming: details are often not communicated. However there was a famous Apple SSL bug that weakened the security of iOS. The statement that led to this bug was a wrong goto statement.

Some argue that the root cause of the bug was not the goto statement in itself, but a wrong copy/paste, a misleading indentation, missing curly braces around the conditional block, or perhaps the working habits of the developer. I can't neither confirm any of them: all these arguments are probable hypotheses and interpretation. Nobody really knows. (meanwhile, the hypothesis of a merge that went wrong as someone suggested in the comments seems to be a very good candidate in view of some other indentation inconsistencies in the same function).

The only objective fact is that a duplicated goto led to exit the function prematurely. Looking at the code, the only other single statement that could have caused the same effect would have been a return.

The error is in function SSLEncodeSignedServerKeyExchange() in this file:

    if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)
        goto fail;
    if ((err =...) !=0)
        goto fail;
    if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
        goto fail;
        goto fail;  // <====OUCH: INDENTATION MISLEADS: THIS IS UNCONDITIONDAL!!
    if (...)
        goto fail;

    ... // Do some cryptographic operations here

fail:
    ... // Free resources to process error

Indeed curly braces around the conditional block could have prevented the bug:
it would have led either to a syntax error at compilation (and hence a correction) or to a redundant harmless goto. By the way, GCC 6 would be able to spot these errors thanks to its optional warning to detect inconsistent indentation.

But in first place, all these gotos could have been avoided with more structured code. So goto is at least indirectly a cause of this bug. There are at least two different ways that could have avoided it:

Approach 1: if clause or nested ifs

Instead of testing lots of conditions for error sequentially, and each time sending to a fail label in case of problem, one could have opted for executing the cryptographic operations in an if-statement that would do it only if there was no wrong pre-condition:

    if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) == 0 &&
        (err = ...) == 0 ) &&
        (err = ReadyHash(&SSLHashSHA1, &hashCtx)) == 0) &&
        ...
        (err = ...) == 0 ) )
    {
         ... // Do some cryptographic operations here
    }
    ... // Free resources

Approach 2: use an error accumulator

This approach is based on the fact that almost all the statements here call some function to set an err error code, and execute the rest of the code only if err was 0 (i.e., function executed without error). A nice safe and readable alternative is:

bool ok = true;
ok =  ok && (err = ReadyHash(&SSLHashSHA1, &hashCtx))) == 0;
ok =  ok && (err = NextFunction(...)) == 0;
...
ok =  ok && (err = ...) == 0;
... // Free resources

Here, there is not a single goto: no risk to jump to quickly to the failure exit point. And visually it would be easy to spot a misaligned line or a forgotten ok &&.

This construct is more compact. It is based on the fact that in C, the second part of a logical and (&&) is evaluated only if the first part is true. In fact, the assembler generated by an optimizing compiler is almost equivalent to the original code with gotos: The optimizer detects very well the chain of conditions and generate code, which at the first non null return value jumps to the end (online proof).

You could even envisage a consistency check at the end of the function that could during the testing phase identify mismatches between the ok flag and the error code.

assert( (ok==false && err!=0) || (ok==true && err==0) );

Mistakes such of a ==0 inadvertently replaced with a !=0 or logical connector errors would easily be spotted during the debugging phase.

As said: I don't pretend that alternative constructs would have avoided any bug. I just want to say that they could have made the bug more difficult to occur.

  • 33
    No, the Apple goto fail bug was caused by a clever programmer deciding not to include the curly braces after an if statement. :-) So when the next maintenance programmer (or the same one, same difference) came along and added another line of code that should have only executed when the if statement evaluated true, the statement ran every time. Bam, the "goto fail" bug, which was a major security bug. But it had essentially nothing to do with the fact that a goto statement was used. – Craig Oct 23 '16 at 23:18
  • 46
    The Apple "goto fail" bug was directly caused by a line of code being duplicated. The particular effect of that bug was caused by that line being a goto inside a brace-less if statement. But if you're suggesting that avoiding goto will make your code immune to the effects of randomly duplicated lines, I have bad news for you. – immibis Oct 23 '16 at 23:43
  • 12
    You're using shortcut boolean operator behavior to execute a statement via side-effect and claiming that's more clear than an if statement? Fun times. :) – Craig Oct 24 '16 at 7:24
  • 37
    If instead of "goto fail" there had been a statement "failed = true" the exact same thing would have happened. – gnasher729 Oct 24 '16 at 8:06
  • 15
    That bug was caused by a sloppy developer not paying attention to what he or she was doing, and not reading their own code changes. Nothing more, nothing less. Okay, maybe throw in some testing oversights in there too. – Lightness Races in Orbit Oct 24 '16 at 11:33
32

The famous Dijkstra article was written at a time when some programming languages were actually capable of creating subroutines having multiple entry and exit points. In other words, you could literally jump into the middle of a function, and jump out at any place within the function, without actually calling that function or returning from it in the conventional way. That's still true of assembly language. Nobody ever argues that such an approach is superior to the structured method of writing software that we now use.

In most modern programming languages, functions are very specifically defined with one entry and one exit point. The entry point is where you specify the parameters to the function and call it, and the exit point is where you return the resulting value and continue execution at the instruction following the original function call.

Within that function, you ought to be able to do whatever you wish, within reason. If putting a goto or two in the function makes it clearer or improves your speed, why not? The whole point of a function is to sequester a bit of clearly-defined functionality, so that you don't have to think about how it works internally anymore. Once it's written, you just use it.

And yes, you can have multiple return statements inside a function; there's still always one place in a proper function from which you return (the back side of the function, basically). That's not at all the same thing as jumping out of a function with a goto before it has a chance to properly return.

enter image description here

So it's not really about using goto's. It's about avoiding their abuse. Everyone agrees that you can make a terribly convoluted program using gotos, but you can do the same by abusing functions as well (it's just a lot easier to abuse gotos).

For what it's worth, ever since I graduated from line-number-style BASIC programs to structured programming using Pascal and curly-brace languages, I've never had to use a goto. The only time I've been tempted to use one is to do an early exit from a nested loop (in languages that don't support multi-level early exit from loops), but I can usually find another way that is cleaner.

11

What kind of bugs do “goto” statements lead to? Are there any historically significant examples?

I used to use goto statements when writing BASIC programs as a child as a simple way to get for and while loops (Commodore 64 BASIC didn't have while-loops, and I was too immature to learn the proper syntax and usage of for-loops). My code was frequently trivial, but any loop bugs could be immediately attributed to my usage of goto.

I now use primarily Python, a high level programming language that has determined it has no need for goto.

When Edsger Dijkstra declared "Goto considered harmful" in 1968 he did not give a handful of examples where related bugs could be blamed on the goto, rather, he declared that goto was unnecessary for higher level languages and that it should be avoided in favor of what we consider normal control flow today: loops and conditionals. His words:

The unbridled use of the go to has an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress.
[...]
The go to statement as it stands is just too primitive; it is too much an invitation to make a mess of one's program.

He probably had mountains of examples of bugs from every time he debugged code with goto in it. But his paper was a generalized position statement backed up by proof that goto was unnecessary for higher level languages. The generalized bug is that you may have no ability to statically analyze the code under question.

Code is much harder to statically analyze with goto statements, especially if you jump back in your control flow (which I used to do) or to some unrelated section of code. Code can and was written this way. It was done to highly optimize for very scarce computing resources and thus the architectures of the machines.

An Apocryphal Example

There was a blackjack program that a maintainer found to be quite elegantly optimized but also impossible for him to "fix" due to the nature of the code. It was programmed in machine code which is heavily reliant on gotos - so I think this story quite relevant. This is the best canonical example I can think of.

A Counter-Example

However, the C source of CPython (the most common and reference Python implementation) uses goto statements to great effect. They are used to bypass irrelevant control flow inside functions to get to the end of functions, making the functions more efficient without losing readability. This respects one of the ideals of structured programming - to have a single exit point for functions.

For the record, I find this counter-example to be quite easy to statically analyze.

  • 5
    The "Story of Mel" I've encountered was on a strange architecture where (1) every machine language instruction does a goto after its operation, and (2) it was terribly inefficient to place a sequence of instructions in consecutive memory locations. And the program was written in hand-optimized assembly, not a compiled language. – Hurkyl Oct 24 '16 at 4:23
  • @MilesRout I think you could be correct here, but the wikipedia page on structured programming says: "The most common deviation, found in many languages, is the use of a return statement for early exit from a subroutine. This results in multiple exit points, instead of the single exit point required by structured programming." - you're saying this is incorrect? Do you have a source to cite? – Aaron Hall Oct 26 '16 at 14:23
  • @AaronHall That's not the original meaning. – Miles Rout Oct 27 '16 at 9:45
9

When the wigwam was current architectural art, their builders could undoubtedly give you sound practical advice regarding construction of wigwams, how to let smoke escape, and so on. Fortunately, today's builders can probably forget most of that advice.

When the stagecoach was current transportational art, their drivers could undoubtedly give you sound practical advice regarding horses of stagecoaches, how to defend against highwaymen, and so on. Fortunately, today's motorists can forget most of that advice, too.

When punched cards were current programming art, their practitioners could likewise give you sound practical advice regarding organization of cards, how to number statements, and so on. I am not sure that that advice is very relevant today.

Are you old enough even to know the phrase "to number statements"? You don't need to know it, but if you don't, then you aren't familiar with the historical context in which the warning against goto was principally relevant.

The warning against goto is just not very relevant today. With basic training in while/for loops and function calls, you won't even think to issue a goto very often. When you do think of it, you probably have a reason, so go ahead.

But can the goto statement not be abused?

Answer: sure, it can be abused, but its abuse is a pretty tiny problem in software engineering compared to far, far more common mistakes such as using a variable where a constant will serve, or such as cut-and-paste programming (otherwise known as neglect to refactor). I doubt that you're in much danger. Unless you are using longjmp or otherwise transferring control to faraway code, if you think to use a goto or you would just like to try it for fun, go ahead. You'll be fine.

You may notice the lack of recent horror stories in which goto plays the villain. Most or all of these stories seem to be 30 or 40 years old. You stand on solid ground if you regard those stories as mostly obsolete.

  • 1
    I see that I've attracted at least one downvote. Well, that's to be expected. More downvotes may come. I'm not going to give the conventional answer, though, when decades of programming experience have taught me that, in this instance, the conventional answer happens to be wrong. This is my view, at any rate. I have given my reasons. The reader can decide. – thb Oct 25 '16 at 16:55
  • 1
    Your post is good too bad I can't downvote comments. – Pieter B Nov 15 '16 at 9:58
6

goto is harder for humans to reason about than other forms of flow control.

Programming correct code is hard. Writing correct programs is hard, determining if programs are correct is hard, proving programs correct is hard.

Getting code to vaguely do what you want is easy compared to everything else about programming.

goto solves some programs with getting code to do what you want. It does not help make checking for correctness easier, while its alternatives often do.

There are styles of programming where goto is the appropriate solution. There are even styles of programming where its evil twin, comefrom, is the appropriate solution. In both of these cases, extreme care has to be done to ensure you are using it in an understood pattern, and lots of manual checking that you aren't doing something difficult to ensure correctness with it.

As an example, there is a feature of some languages called coroutines. Coroutines are threadless threads; execution state without a thread to run it on. You can ask them to execute, and they can run part of themselves and then suspend themselves, handing back flow control.

"Shallow" coroutines in languages without coroutine support (like C++ pre-C++20 and C) are possible using a mixture of gotos and manual state management. "Deep" coroutines can be done using the setjmp and longjmp functions of C.

There are cases where coroutines are so useful that writing them manually and carefully is worth it.

In the case of C++, they are being found useful enough that they are extending the language to support them. The gotos and manual state management is being hidden behind a zero cost abstraction layer, permitting programmers to write them without the difficulty of having to prove their mess of goto, void**s, manual construction/destruction of state, etc is correct.

The goto gets hidden behind a higher level abstraction, like while or for or if or switch. Those higher level abstractions are easier to prove correct and check.

If the language was missing some of them (like some modern languages are missing coroutines), either limpling along with a pattern that doesn't fit the problem, or using goto, becomes your alternatives.

Getting a computer to vaguely do what you want it to do in common cases is easy. Writing reliably robust code is hard. Goto helps the first far more than it helps the second; hence "goto considered harmful", as it is a sign of superficially "working" code with deep and hard to track down bugs. With sufficient effort you can still make code with "goto" reliably robust, so holding the rule of as absolute is wrong; but as a rule of thumb, it is a good one.

  • 1
    longjmp is only legal in C for unwinding the stack back to a setjmp in a parent function. (Like breaking out of multiple levels of nesting, but for function calls instead of loops). longjjmp to a context from a setjmp done in a function that has since returned is not legal (and I suspect won't work in practice in most cases). I'm not familiar with co-routines, but from your description of a co-routine as similar to a user-space thread, I think it would need its own stack as well as a saved-register context, and longjmp only gives you register-saving, not a separate stack. – Peter Cordes Oct 24 '16 at 16:29
  • 1
    Oh, I should have just googled: fanf.livejournal.com/105413.html describes making stack space for the coroutine to run. (Which as I suspected, is necessary on top of just using setjmp/longjmp). – Peter Cordes Oct 24 '16 at 16:33
  • 2
    @PeterCordes: Implementations are not required to provide any means by which code could create a pair jmp_buff that is suitable for use as a coroutine. Some implementations do specify a means via which code may create one even though the Standard doesn't require them to provide such an ability. I would not describe code which relies upon such techniques as "standard C", since in many cases a jmp_buff will be an opaque structure that is not guaranteed to behave consistently between different compilers. – supercat Oct 25 '16 at 0:43
5

To add one thing to the other excellent answers, with goto statements it can be difficult to tell exactly how you got to any given place in the program. You can know that an exception occurred at some specific line but if there are goto's in the code there is no way to tell what statements executed to result in this exception causing state without searching the whole program. There is no call stack and no visual flow. It is possible that there is a statement 1000 lines away which put you in a bad state executed a goto to the line which raised an exception.

  • 1
    Visual Basic 6 and VBA have painful error handling, but if you use On Error Goto errh, you can use Resume to retry, Resume Next to skip the offending line, and Erl to know which line it was (if you use line numbers). – Cees Timmerman Oct 24 '16 at 8:47
  • @CeesTimmerman I've done very little with Visual Basic, I did not know that. I will add a comment about it. – qfwfq Oct 24 '16 at 15:24
  • 2
    @CeesTimmerman: The VB6 semantics of "resume" are horrible if an error occurs within a subroutine that doesn't have its own on-error block. Resume will re-execute that function from the start, and Resume Next will skip everything in the function that hasn't yet. – supercat Oct 24 '16 at 21:49
  • 2
    @supercat Like i said, it's painful. If you need to know the exact line, you should number them all, or temporarily disable the error handling with On Error Goto 0 and manually Debug. Resume is meant to be used after checking Err.Number and making the necessary adjustments. – Cees Timmerman Oct 25 '16 at 8:16
  • 1
    I should note that in a modern language, goto only works with labeled lines, which appears to have been superseded by labeled loops. – Cees Timmerman Oct 25 '16 at 8:25
5

Take a look at this code, from http://www-personal.umich.edu/~axe/research/Software/CC/CC2/TourExec1.1.f.html that was actually part of a large Prisoner's Dilemma simulation. (The spacing got messed up some in transfer; all the line numbers were in the leftmost column and all the statements lined up straight.) If you've seen old FORTRAN or BASIC code, you'll realize it's not that unusual.

C  Not nice rules in second round of tour (cut and pasted 7/15/93)
   FUNCTION K75R(J,M,K,L,R,JA)
C  BY P D HARRINGTON
C  TYPED BY JM 3/20/79
   DIMENSION HIST(4,2),ROW(4),COL(2),ID(2)
   K75R=JA       ! Added 7/32/93 to report own old value
   IF (M .EQ. 2) GOTO 25
   IF (M .GT. 1) GOTO 10
   DO 5 IA = 1,4
     DO 5 IB = 1,2
5  HIST(IA,IB) = 0

   IBURN = 0
   ID(1) = 0
   ID(2) = 0
   IDEF = 0
   ITWIN = 0
   ISTRNG = 0
   ICOOP = 0
   ITRY = 0
   IRDCHK = 0
   IRAND = 0
   IPARTY = 1
   IND = 0
   MY = 0
   INDEF = 5
   IOPP = 0
   PROB = .2
   K75R = 0
   RETURN

10 IF (IRAND .EQ. 1) GOTO 70
   IOPP = IOPP + J
   HIST(IND,J+1) = HIST(IND,J+1) + 1
   IF (M .EQ. 15 .OR. MOD(M,15) .NE. 0 .OR. IRAND .EQ. 2) GOTO 25
   IF (HIST(1,1) / (M - 2) .GE. .8) GOTO 25
   IF (IOPP * 4 .LT. M - 2 .OR. IOPP * 4 .GT. 3 * M - 6) GOTO 25
   DO 12 IA = 1,4
12 ROW(IA) = HIST(IA,1) + HIST(IA,2)

   DO 14 IB = 1,2
     SUM = .0
     DO 13 IA = 1,4
13   SUM = SUM + HIST(IA,IB)
14 COL(IB) = SUM

   SUM = .0
   DO 16 IA = 1,4
     DO 16 IB = 1,2
       EX = ROW(IA) * COL(IB) / (M - 2)
       IF (EX .LE. 1.) GOTO 16
       SUM = SUM + ((HIST(IA,IB) - EX) ** 2) / EX
16 CONTINUE

   IF (SUM .GT. 3) GOTO 25
   IRAND = 1
   K75R = 1
   RETURN

25 IF (ITRY .EQ. 1 .AND. J .EQ. 1) IBURN = 1
   IF (M .LE. 37 .AND. J .EQ. 0) ITWIN = ITWIN + 1
   IF (M .EQ. 38 .AND. J .EQ. 1) ITWIN = ITWIN + 1
   IF (M .GE. 39 .AND. ITWIN .EQ. 37 .AND. J .EQ. 1) ITWIN = 0
   IF (ITWIN .EQ. 37) GOTO 80
   IDEF = IDEF * J + J
   IF (IDEF .GE. 20) GOTO 90
   IPARTY = 3 - IPARTY
   ID(IPARTY) = ID(IPARTY) * J + J
   IF (ID(IPARTY) .GE. INDEF) GOTO 78
   IF (ICOOP .GE. 1) GOTO 80
   IF (M .LT. 37 .OR. IBURN .EQ. 1) GOTO 34
   IF (M .EQ. 37) GOTO 32
   IF (R .GT. PROB) GOTO 34
32 ITRY = 2
   ICOOP = 2
   PROB = PROB + .05
   GOTO 92

34 IF (J .EQ. 0) GOTO 80
   GOTO 90

70 IRDCHK = IRDCHK + J * 4 - 3
   IF (IRDCHK .GE. 11) GOTO 75
   K75R = 1
   RETURN

75 IRAND = 2
   ICOOP = 2
   K75R = 0
   RETURN

78 ID(IPARTY) = 0
   ISTRNG = ISTRNG + 1
   IF (ISTRNG .EQ. 8) INDEF = 3
80 K75R = 0
   ITRY = ITRY - 1
   ICOOP = ICOOP - 1
   GOTO 95

90 ID(IPARTY) = ID(IPARTY) + 1
92 K75R = 1
95 IND = 2 * MY + J + 1
   MY = K75R
   RETURN
   END

There's a lot of issues here that go well beyond the GOTO statement here; I honestly think the GOTO statement was a bit of scapegoat. But the control flow is absolutely not clear here, and code is mixed together in ways that make it very unclear what's going on. Even without adding comments or using better variable names, changing this to a block structure without GOTOs would make it much easier to read and follow.

  • 4
    so true :-) May be worth to mention: legacy FORTAN and BASIC didn't have block structures, so that if a THEN clause could not hold on one line GOTO was the only alternative. – Christophe Oct 24 '16 at 18:12
  • Text labels instead of numbers, or at least comments on the numbered lines, would help a lot. – Cees Timmerman Oct 25 '16 at 9:48
  • Back in my FORTRAN-IV days: I restricted my use of GOTO to implement IF/ELSE IF / ELSE blocks, WHILE loops and BREAK and NEXT in loops. Someone showed me the evils of spaghetti code and why you should structure to avoid it even if you have to implement your block structures with GOTO. Later I started using a pre-processor (RATFOR, IIRC). Goto is not intrinsically evil, it's just an over-powerful low-level construct that maps to an assembler branch instruction. If you have to use it because your language is deficient, use it to build or augment the proper block structures. – nigel222 Oct 25 '16 at 10:06
  • @CeesTimmerman: That is garden-variety FORTRAN IV code. Text labels were not supported in FORTRAN IV. I do not know whether the more recent versions of the language support them. Comments on the same line as code were not supported in standard FORTRAN IV, although there may have been vendor-specific extensions to support them. I do not know what the "current" FORTRAN standard says: I abandoned FORTRAN a long time ago, and I do not miss it. (The most recent FORTRAN code I've seen bears a strong resemblance to early-1970s PASCAL.) – John R. Strohm Oct 25 '16 at 18:44
  • 1
    @CeesTimmerman: Vendor-specific extension. The code in general is REALLY shy on comments. Also, there's a convention that became common some places, of putting line numbers only on CONTINUE and FORMAT statements, to make it easier to add lines later. This code doesn't follow that convention. – John R. Strohm Oct 25 '16 at 19:39
0

One of the principles of maintainable programming is encapsulation. The point is that you interface with a module/routine/subroutine/component/object by using a defined interface, and only that interface, and the results will be predictable (assuming that the unit was effectively tested).

Within a single unit of code, the same principle applies. If you consistently apply structured programming or object-oriented programming principles, you will not:

  1. create unexpected paths through the code
  2. arrive in sections of code with undefined or impermissible variable values
  3. exit a path of the code with undefined or impermissible variable values
  4. fail to complete a transaction unit
  5. leave portions of code or data in memory that are effectively dead, but ineligible for garbage cleanup and reallocation, because you have not signalled their release using an explicit construct that invokes releasing them as the code control path exits the unit

Some of the more common symptoms of these processing errors include memory leaks, memory hoarding, pointer overflows, crashes, incomplete data records, record add / chg / delete exceptions, memory page faults, and so on.

The user-observable manifestations of these problems include user interface lock-up, gradually decreasing performance, incomplete data records, inability to initiate or complete transactions, corrupted data, network outages, power outages, embedded systems failures (from the loss of control of missiles to the loss of tracking and control ability in air traffic control systems), timer failures, and so on and so on. The catalog is very extensive.

  • 3
    You answered without mentioning goto even once. :) – Robert Harvey Oct 24 '16 at 23:03
0

goto is dangerous because it is usually used where not needed. Using anything that is not needed is dangerous, but goto especially. If you google you will find lots of errors around caused by goto, this is not per-se a reason to not using it (bugs always happens when you use language features because that's intrinsic in programming), but some of them are clearly highly related to goto usage.


Reasons to use/not use goto:

  • If you need a loop, you have to use while or for.

  • If you need to do conditional jump, use if/then/else

  • If you need procedure, call a function/method.

  • If you need to quit a function, just return.

I can count on my fingers places where I've seen goto used and used properly.

  • CPython
  • libKTX
  • probably few more

In libKTX there is a function that has the following code

if(something)
    goto cleanup;

if(bla)
    goto cleanup;

cleanup:
    delete [] array;

Now in this place goto is usefull because the language is C:

  • we are inside a function
  • we cannot write a cleanup function (because we enter another scope, and making accessible caller function state is more burden)

This use case is usefull because in C we do not have classes, so the simplest way to cleanup is using a goto.

If we had the same code in C++ there's no need for goto anymore:

class MyClass{
    public:

    void Function(){
        if(something)
            return Cleanup(); // invalid syntax in C#, but valid in C++
        if(bla)
            return Cleanup(); // invalid syntax in C#, but valid in C++
    }

    // access same members, no need to pass state (compiler do it for us).
    void Cleanup(){

    }



}

What kind of bugs it can lead to? Anything. Infinite loops, wrong order of execution, stack screw..

A documented case is a vulnerability in SSL that allowed Man in the middle attacks caused by wrong use of goto: here's the article

That's a typo error, but went unnoticed for a while, if the code were structured in an other way, tested properly such error could have not been possible.

  • 1
    There's lots of comments to one answer that explain very clearly that the use of "goto" was totally irrelevent to the SSL bug - the bug was caused by careless duplication of one line of code, so it was once executed conditionally, and once unconditionally. – gnasher729 Oct 26 '16 at 23:08
  • Yeah I'm waiting for a better historical example of a goto bug before I accept. As said; It really wouldn't matter what was on that line of code; The lack of curly braces would have executed it and caused an error regardless. I am curious though; what is a "Stack Screw"? – Akiva Oct 26 '16 at 23:28
  • 1
    An example from code I worked on previously in PL/1 CICS. Program put out a screen consisting of single line maps. When the screen was returned it found the array of maps it had sent. Used the elements of that to find the index in one array, and then used that index to an index of array variables to do a GOTO to so it could process that individual map. If the array of maps sent was incorrect (or had been corrupted) then it could try to do a GOTO to the wrong label, or worse crash because it accessed an element after the end of the array of label variables. Rewritten to use case type syntax. – Kickstart Oct 27 '16 at 11:16

protected by gnat Oct 25 '16 at 5:24

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.