To Goto or Not to Goto

To GOTO or not to GOTO?

I am not sure what do you mean by clean up code but in C++ there is a concept called "resource acquisition is initialization" and it should be the responsibility of your destructors to clean up stuff.

(Note that in C# and Java, this is usually solved by try/finally)

For more info check out this page:
http://www.research.att.com/~bs/bs_faq2.html#finally

EDIT: Let me clear this up a little bit.

Consider the following code:

void MyMethod()
{
MyClass *myInstance = new MyClass("myParameter");
/* Your code here */
delete myInstance;
}

The problem: What happens if you have multiple exits from the function? You have to keep track of each exit and delete your objects at all possible exits! Otherwise, you will have memory leaks and zombie resources, right?

The solution: Use object references instead, as they get cleaned up automatically when the control leaves the scope.

void MyMethod()
{
MyClass myInstance("myParameter");
/* Your code here */
/* You don't need delete - myInstance will be destructed and deleted
* automatically on function exit */
}

Oh yes, and use std::unique_ptr or something similar because the example above as it is is obviously imperfect.

Why does Go have a goto statement?

When we actually check the source code of the Go standard library, we can see where gotos are actually well applied.

For example, in the math/gamma.go file, the goto statement is used:

  for x < 0 {
if x > -1e-09 {
goto small
}
z = z / x
x = x + 1
}
for x < 2 {
if x < 1e-09 {
goto small
}
z = z / x
x = x + 1
}

if x == 2 {
return z
}

x = x - 2
p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
return z * p / q

small:
if x == 0 {
return Inf(1)
}
return z / ((1 + Euler*x) * x)
}

The goto in this case saves us from introducing another (boolean) variable used just for control-flow, checked for at the end. In this case, the goto statement makes the code actually better to read and easier follow (quite in contrary to the argument against goto you mentioned).

Also note, that the goto statement has a very specific use-case. The language specification on goto states that it may not jump over variables coming into scope (being declared), and it may not jump into other (code-)blocks.

Why is it not good practice to use 'goto'?

Spaghetti is a code poorly structured and which makes it hard to update because of multiple undocumented and unexpected links or dependencies. You touch it in one place and you see other things get broken or modified in an unexpected way. Just like sticky spaghetti, you pull one end and you see a number of places start moving.
GOTO usually violates the principles of structured, procedural programming, hence the term, which suggests the tangled and arbitrary nature of the program flow.

To use goto or not?

Using a goto for implementing a state machine often makes good sense. If you're really concerned about using a goto, a reasonable alternative is often to have a state variable that you modify, and a switch statement based on that:

typedef enum {s0,s1,s2,s3,s4,...,sn,sexit} state;

state nextstate;
int done = 0;

nextstate = s0; /* set up to start with the first state */
while(!done)
switch(nextstate)
{
case s0:
nextstate = do_state_0();
break;
case s1:
nextstate = do_state_1();
break;
case s2:
nextstate = do_state_2();
break;
case s3:
.
.
.
.
case sn:
nextstate = do_state_n();
break;
case sexit:
done = TRUE;
break;
default:
/* some sort of unknown state */
break;
}

What is wrong with using goto?

Because they lead to spaghetti code.

In the past, programming languages didn't have while loops, if statements, etc., and programmers used goto to make up the logic of their programs. It lead to an unmaintainable mess.

That's why the CS gods created methods, conditionals and loops. Structured programming was a revolution at the time.

gotos are appropriate in a few places, such as for jumping out of nested loops.

How to use goto instead of for, while, or switch

Consider the first part of your first example:

if (i > 5)
{
i++;
}

Where does the control flow go if i <= 5? Where does the control flow go if i > 5? -- you should make sure you can answer this before you read on.

A direct translation would be

if (i > 5)
goto if-body;
goto end-if
if-body:
i++;
end-if:
....

You should convince yourself that the control flow in the above is the same as the original. You can simplify this a bit by flipping the condition:

if(i <= 5)
goto end-if;
i++;
end-if:
....

Again, you should convince yourself that the control flow is still the same.

From this and what Brandin gave in his answer, you should be able to do the rest of the assignment.

Understand the pro and cons of the GoTo statement

Your code can be easily re-written as below:

Private Sub SaveButton_Click()

Dim i As Integer

'mandatory textboxes:
For i = 1 To 13
If UserForm1.Controls("Textbox" & i) = "" Then
MsgBox "Please fill in every mandatory textbox"
Exit Sub
End If
Next

End Sub

Don't ever use GoTo unless it is behind On Error … or not avoidable. If there is any chance to avoid GoTo, then avoid it. It makes your code hard to maintain and is considered to be a bad practice.

As GSerg pointed out there might be rare cases where GoTo cannot be avoided. Eg. using GoTo for emulating missing language constructs (e.g. VBA lacks the Continue keyword) and exiting deeply nested loops prematurely.

GOTO still considered harmful?

The following statements are generalizations; while it is always possible to plead exception, it usually (in my experience and humble opinion) isn't worth the risks.

  1. Unconstrained use of memory addresses (either GOTO or raw pointers) provides too many opportunities to make easily avoidable mistakes.
  2. The more ways there are to arrive at a particular "location" in the code, the less confident one can be about what the state of the system is at that point. (See below.)
  3. Structured programming IMHO is less about "avoiding GOTOs" and more about making the structure of the code match the structure of the data. For example, a repeating data structure (e.g. array, sequential file, etc.) is naturally processed by a repeated unit of code. Having built-in structures (e.g. while, for, until, for-each, etc.) allows the programmer to avoid the tedium of repeating the same cliched code patterns.
  4. Even if GOTO is low-level implementation detail (not always the case!) it's below the level that the programmer should be thinking. How many programmers balance their personal checkbooks in raw binary? How many programmers worry about which sector on the disk contains a particular record, instead of just providing a key to a database engine (and how many ways could things go wrong if we really wrote all of our programs in terms of physical disk sectors)?

Footnotes to the above:

Regarding point 2, consider the following code:

    a = b + 1
/* do something with a */

At the "do something" point in the code, we can state with high confidence that a is greater than b. (Yes, I'm ignoring the possibility of untrapped integer overflow. Let's not bog down a simple example.)

On the other hand, if the code had read this way:

    ...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

The multiplicity of ways to get to label 10 means that we have to work much harder to be confident about the relationships between a and b at that point. (In fact, in the general case it's undecideable!)

Regarding point 4, the whole notion of "going someplace" in the code is just a metaphor. Nothing is really "going" anywhere inside the CPU except electrons and photons (for the waste heat). Sometimes we give up a metaphor for another, more useful, one. I recall encountering (a few decades ago!) a language where

    if (some condition) {
action-1
} else {
action-2
}

was implemented on a virtual machine by compiling action-1 and action-2 as out-of-line parameterless routines, then using a single two-argument VM opcode which used the boolean value of the condition to invoke one or the other. The concept was simply "choose what to invoke now" rather than "go here or go there". Again, just a change of metaphor.

Are there any legitimate use-cases for goto in a language that supports loops and functions?

There are a few reasons for using the "goto" statement that I'm aware of (some have spoken to this already):

Cleanly exiting a function

Often in a function, you may allocate resources and need to exit in multiple places. Programmers can simplify their code by putting the resource cleanup code at the end of the function, and all "exit points" of the function would goto the cleanup label. This way, you don't have to write cleanup code at every "exit point" of the function.

Exiting nested loops

If you're in a nested loop and need to break out of all loops, a goto can make this much cleaner and simpler than break statements and if-checks.

Low-level performance improvements

This is only valid in perf-critical code, but goto statements execute very quickly and can give you a boost when moving through a function. This is a double-edged sword, however, because a compiler typically cannot optimize code that contains gotos.

Note that in all these examples, gotos are restricted to the scope of a single function.

Why and when should a goto be used in C / C++?

There is no circumstance under which a goto is strictly necessary: you can always avoid it if you really want to, but to do that for purely idealogical reasons is a mistake.

For example, a friend of mine wrote a wavelet transform function that had (something like) 15 nested loops. In the event of an error in those loops, he had a goto to a cleanup block just before the function's return statement. To achieve the same effect without a goto would have involved setting a flag and testing it at every loop level, which would have made his code far less readable. In these circumstances, goto was the right choice.



Related Topics



Leave a reply



Submit