gotos

SuperUser root at mfci.UUCP
Thu Apr 14 11:28:00 AEST 1988


Expires:

Sender:

Followup-To:

Distribution:

Keywords:


I feel that taking a near religious stance on the prohibition of
something like goto is a mistake, although I can understand why
people are tempted.  Few would argue that the proper use of
functions, compound statements, and flow control statements isn't
preferable to long rambling routines whose control structures
resemble a tangled ball of fishing line.  Avoiding the use of gotos
is probably a good rule of thumb for beginning programmers or
programmers who have only used languages like Basic or Fortran,
to get them used to the fact that they can do just about anything
without using gotos and without contorting their program structure
(avoiding gotos usually improves program structure).

However, the most important goals in writing code are that it
functions properly, that it is clearly written and maintainable,
and that it is reasonably efficient.  Of course, there may sometimes
be a tradeoff between the last two goals, but in many cases where a
goto may be appropriate it is often in the interests of both these
goals.

The most common place where the use of a goto is not inappropriate in
C is when it is used to exit multiple levels of nested loops or other
such control structures.  Some languages permit constructs such as
"break n" to accomplish this, but I generally consider this sort of
practice to be bad, because it is painful to maintain.  I.e., if you
wanted to wrap it in a loop, or remove a loop from around it, you
would have to delve into the code to alter the "level" of the break.
I prefer to achieve the effect of a the LEAVE expression of Bliss.
This is a STRUCTURED construct, in that it cannot be used to create
irreducible flow graphs (all Bliss flow graphs are reducible).  It
can only be used to exit an expression from anywhere within the
expression, and is no different from returning from a routine within
a loop, except that you are merely exiting from a statement rather
than the entire routine.  In C, the same effect can be achieved by
placing a label right after a loop, then branching to the label
from within one or more nested loops.  The resulting flow graph
is still reducible, and avoids the use of flow control flags, which
almost always obscure the code, and often slow it down as well.
(This isn't to say that I think flow control flags are always
inappropriate, but in most typical cases it is preferable to replace
them with explicit exits, via goto.)

Many other uses of goto are probably best avoided, such as branching into
loops.  Often the need to branch into a loop arises because the loop
is naturally a "mid-tested" loop, in which case it can be rewritten
as a for(;;) { ... } loop, with an explicit exit test in the middle.
In cases where the loop really has multiple entry points, it is up to
the implementor to decide whether it can be restructured to avoid the
problem, or whether it's worth explicitly adjusting the various cases
before the loop so they can all enter at the same point, or whether
it's worth entirely replicating the loop, etc.  However, people should
realize that if their code incorporates irreducible loops, their compiler
may not do a good job of optimizing it.  I have seen rare examples where
an irreducible loop is the clearest way to code something, although even
in those cases it is usually preferable to avoid them.  However, I would
not go so far as to say they are 100% wrong and should NEVER be used.
For example, I have seen people implement small state machines with
gotos, where each state explicity branches to the next state, rather
than having to go through the overhead of a switch statement at the
top of an outer loop.  This can be useful in small, simple lexical
analyzers.  True, keeping the state in a variable which is used in
a switch statement probably isn't that much slower, and could even
be faster in some cases (if the compiler does a better job of optimizing
reducible loops), but nevertheless it may not always be the best choice.

The other case I can think of where a goto might be appropriate is if
you have a complicated loop which is making some determination about
something, and at various points in the loop it may "accept" or "reject"
something, in which case you'd like to go to some common piece of code to
perform the necessary bookkeeping.  Ideally the loop would be contained
in a routine which simply returns true or false and leaves it up to the
caller to take whatever action is appropriate, but again this may not
always be practical.  In this case I would argue that the code being
branched to should be placed outside of the code that branches to it
(rather than coding it as the consequent of one test and then branching
to it from the others).  However, the end of it should not turn around
and branch back into a loop or whatever, but should instead return
from the routine or fall through to the bottom of a loop or to some
ordinary sequential code which follows it, etc.

All this isn't meant to imply that I like gotos and that they don't bother
me.  I very rarely use them, and in cases where I do it is because I have
carefully investigated the alternatives and been unable to eliminate
them without compromising the control structure of my code in even
less desirable ways.  Nevertheless, if used with restraint they do
have their places.



More information about the Comp.lang.c mailing list