Path: utzoo!mnetor!uunet!mfci!root From: root@mfci.UUCP (SuperUser) Newsgroups: comp.lang.c Subject: Re: gotos Message-ID: <338@m3.mfci.UUCP> Date: 14 Apr 88 01:28:00 GMT References: <1988Apr11.201934.20594@utzoo.uucp> <451@goofy.megatest.UUCP> Reply-To: karzes@m3.UUCP (Tom Karzes) Organization: Multiflow Computer Inc., Branford Ct. 06405 Lines: 85 Summary: 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.