weiss@bigburd.PRC.Unisys.COM (Tom Weiss) (07/15/87)
Since now appears to be the time to express opinions about style/clarity, I would like to address my own pet peeve: the goto statement. There are very few legitimate uses for the goto statement, and C provides alternates for most of them. While it is true that these alternates are no less evil than goto viewed from a structured programming perspective, at least they are more readable. Since C provides the break statement to break out of a loop, the continue statement to jump back to the top of a loop, the return statement to immediately exit a function, and access to system calls (e.g. exit) that can terminate execution in error handling functions, there are few 'legitimate' uses for goto. The only one that immediately comes to mind is breaking directly out of a multiply-nested control structure. Once I was involved in rewriting a piece of code which contained a goto. The code consisted of a large loop containing switch and if/else statements. When I first rewrote it, I eliminated the original goto, but introduced two of my own! I then took a close look at what was really going on. The original goto was used to jump forward into the middle of an if/else construct. When I rewrote the code, I basically added features, without changing the overall control flow. Though the original goto disappeared, I introduced more goto's to keep in tune with the original structure. When I saw what I had done, I knew something was very wrong, and I decided to see what would happen if I rewrote the code to remove the goto's. I originally expected that I would wind up making some new functions to be called from the loop. As it turned out, all I had to do was change the order of tests in the loop (this involved eliminating the switch). In short, poor logical design had necessitated the original use of goto, and encouraged more goto's in expanded versions. My point is that 99% of the time I see a goto in a C program, it is not there for any good reason. Usually goto's are used to make loops or jump around within a chunk of code that has poorly structured control flow. I do not mean to say that programmers who use goto's are lousy programmers, merely that they have gotten into a bad habit that should be broken.
hwe@beta.UUCP (Skip Egdorf) (07/17/87)
In article <3289@bigburd.PRC.Unisys.COM>, weiss@bigburd.PRC.Unisys.COM (Tom Weiss) writes: > > Since now appears to be the time to express opinions about > style/clarity, I would like to address my own pet peeve: the goto > statement. I love your comments Tom, now just be a bit more assertive... > > There are very few legitimate uses for the goto statement, There are NO legitimate uses for the goto statement... > ... C > provides alternates for most of them. While it is true that these > alternates are no less evil than goto viewed from a structured > programming perspective, at least they are more readable. A large myth from the 70's, where structured programming was viewed as "no gotos", was that forward jumps out of loops were not structured. This is not true. A break transfers control to a known place and provides just as structured a piece of code as a while leaving the loop on a false condition. I have been in "discussions" with programmers who claim that some piece of code just written, containing only gotos as structuring elements, is a structured program because it uses only proper constructs (implemented with gotos) of if-then-else, looping, and sequences. This argument is valid as far as it goes. Two problems exist with programs of this form, or programs with ANY gotos. 1. The major amount of effort in any production code is maintaining that code. As programmers modify code with gotos, lack of understanding of the entire structure of the program leads to local modifications. Additional jumps to local labels, and additions of code that span earlier structures not visible due to the goto-implementation cause Entropy increase, and the code becomes unstructured. 2. While the author claims that the program is structured, no compiler can verify that the gotos are used to implement only the structured programming constructs. I don't believe my own assertions about the correctness of some un-verified piece of my own code. I will not believe someone else's. > > Since C provides the break statement to break out of a loop, the > continue statement to jump back to the top of a loop, the return > statement to immediately exit a function, and access to system calls > (e.g. exit) that can terminate execution in error handling functions, > there are few 'legitimate' uses for goto. The only one that > immediately comes to mind is breaking directly out of a > multiply-nested control structure. And if such a structure is used, it can ALWAYS be improved by removing some of the structure to a sub-function. > > Once I was involved in rewriting a piece of code which contained a > goto. > ... story about re-doing a code containing gotos, and improving it... > Again, two points 1. When I moved from fortran and assembler to a MULTICS and PL/I, I found that I had stopped using gotos. I didn't do this on purpose. I just found that I never reached a place where I needed one. I began examining lots of other code and re-doing it without gotos. Sometimes this was hard, and involved a total re-design of the code (see below). I did find that in EVERY case the code got more understandable, more efficient, and shorter in both source and object form. EVERY case was a triple win, with no loss. I have never found a case where someone could provide me with a goto'ed code, and I could not improve it in all three areas. Anyone reading this who sends me some piece of garbage to improve, please understand that it will goto(sic) /dev/null. The reson is... 2. Just removing gotos by simple restructuring techniques is not sufficient. (If it were, a compiler could do it. Recall that this is the general case. Not just a few special cases, such as handeled by programs like 'struct'.) Goto lovers find it fairly simple to come up with code fragments that require extra state variables or extra levels of procedure call to improve. My removal of gotos sometimes required my return to the functional specification of the system and a complete re-implementation. (and yes, I re-implemented as little as necessary, so that this did not skew the results too badly. I do believe that the re-implementation affected only the goto-ness of the program.) I finally gave up on this as I felt, and now feel, that any program with a goto is poorly designed, badly implemented, and a sign that the programmer did not know what was going on. To finish up... In 1968 Dijkstra's "gotos considered harmful" letter in CACM caused comments like "I don't really understand what he is saying, but I am sure that it is important...". Computer professionals who did not wish to be branded as fuzzy-headed ivory-tower unrealistic dreamers were forced to take a moderating position of "gotos are bad, but a few are ok, once in a while...". It is time to reverse this. So, to make my position clear: In any language that supports a complete set of structured constructs, there is NO NEED for a goto, and the statement should be removed from the language! Languages that do not have enough control structure should be used only with a pre-processor (e.g. Fortran and Ratfor). Skip Egdorf hwe@lanl.gov Since most work at Los Alamos is still fortran, I am sure that these views are not representative of my employer. More's the s thip
edw@ius2.cs.cmu.edu (Eddie Wyatt) (07/17/87)
In an article by Skip Egdorf (hwe@beta.UUCP), Skip sez,
> There are NO legitimate uses for the goto statement...
I sez..
There is a whole class of problems that map very nicely into goto contructs.
They are simulation of NFAs and DFAs (ie finite state machines).
States map very nicely to labels and transitions map very nicely into
if (input == ?) goto label.
The most readable way one can represent the NFA/DFA is through a mesh
of gotos with a diagram of the machine in comments :-).
For most other problems though, gotos are not need.
I have to admit though, since my days of BASIC, I haven't used a single
goto statement. The lexigraphical analysizers I've written, have been
simple enough that one case statement with some nicely place returns
will do the job.
--
Eddie Wyatt
e-mail: edw@ius2.cs.cmu.edu
terrorist, cryptography, DES, drugs, cipher, secret, decode, NSA, CIA, NRO.
wesommer@athena.mit.edu (William Sommerfeld) (07/18/87)
In article <7571@beta.UUCP> hwe@beta.UUCP (Skip Egdorf) writes: > There are NO legitimate uses for the goto statement... .. more flaming about "goto", "break", etc. > >1. When I moved from fortran and assembler to a MULTICS and PL/I, I found that > I had stopped using gotos. I didn't do this on purpose. I just found > that I never reached a place where I needed one. Ok, here's a different opinion: gotos are useful only for jumping to cleanup code at the end of a function or block. This is particularly important in kernel and daemon code, where avoiding memory leaks and forgotten unlocks of interlocked objects means that you need to do some cleanup before returning. Sure, you can put all that stuff in line before the return statement, but when you add something else which needs cleanup to the function, you have to go through all the error returns, and fill in the things which need allocating.. by having one error return reached by "goto punt" or "goto bad", you put one allocate/lock/etc call at the start of the routine, and one free/unlock/etc call at the end. i.e.: frobnicate(name1, name2) char *name1, *name2; { object *foo = NULL, *bar = NULL; int status; /* return code; always set before a "goto punt" */ foo = find_object(name1); if (!foo) { status = NO_FOO; goto punt; } bar = find_object(name2); if (!bar) { status = NO_BAR; goto punt; } status = mush(foo, bar); if (status) goto punt; status = mush(bar, foo); if (status) goto punt; status = mash(foo); punt: if (foo) release(foo); if (bar) release(bar); return status; } (if you replace "object" with "inode" and "find_object" with "namei", and you've got a typical UNIX system call) Interestingly enough, this coding style opinions evolved mostly from looking at Unix kernel code and (get this) Multics PL/1 code that a friend of mine was working on. >In any language that supports a complete set of structured constructs, >there is NO NEED for a goto, and the statement should be removed from >the language! What this use of the goto boils down to is an exceptional return. C doesn't have any kind of exception handling mechanism, which is a part of any "complete set of structured constructs", so it is necessary to simulate exception handling using goto. Most multics PL/1 programs I've seen don't use PL/1 exceptions very much; return codes and "goto PUNT" are much more common, probably for efficiency reasons. Exceptions are used for some really obscure things, though.. the library routine which asks a yes-or-no question first signals "command_query", and if there is a hander for it, it can answer the question. This is used by the 'answer' command, as in (to use a UNIXy example) "answer yes fsck /dev/rra0g" [Never mind fsck -y; this is a contrived example]. Since on Multics, I/O redirection is a mess and pipes don't exist, "yes | fsck /dev/rra0g" is out of the question. Bill Sommerfeld wesommer@athena.mit.edu ...!mit-eddie!wesommer
daveb@geac.UUCP (Dave Brown) (07/18/87)
It is as much a design problem as a style issue: the same keyword is used to exit a switch as a loop, therefore making it impossible to exit a loop from said switch.... Unless you really distort things and use continue to mean "don't break". B had the same problem, not unexpectedly. -- David (Collier-) Brown. | Computer Science Geac Computers International Inc., | loses its memory 350 Steelcase Road,Markham, Ontario, | (if not its mind) CANADA, L3R 1B3 (416) 475-0525 x3279 | every 6 months.
ark@alice.UUCP (07/18/87)
Skip, I suggest you read Don Knuth's article "Structured Programming with goto Statements" in Computing Surveys (Jan 1974?).
mlinar@poisson.usc.edu (Mitch Mlinar) (07/18/87)
In article <7571@beta.UUCP> hwe@beta.UUCP (Skip Egdorf) writes: >In article <3289@bigburd.PRC.Unisys.COM>, weiss@bigburd.PRC.Unisys.COM (Tom Weiss) writes: >> >> Since now appears to be the time to express opinions about >> style/clarity, I would like to address my own pet peeve: the goto >> statement. > >I love your comments Tom, now just be a bit more assertive... > >> >> There are very few legitimate uses for the goto statement, > > There are NO legitimate uses for the goto statement... > Hmmmm. Methinks I smells a fanatic ... >> >> Since C provides the break statement to break out of a loop, the >> continue statement to jump back to the top of a loop, the return >> statement to immediately exit a function, and access to system calls >> (e.g. exit) that can terminate execution in error handling functions, >> there are few 'legitimate' uses for goto. The only one that >> immediately comes to mind is breaking directly out of a >> multiply-nested control structure. > >And if such a structure is used, it can ALWAYS be improved by removing >some of the structure to a sub-function. > which also clearly increases the code size (even if only a little), but let's read on > more efficient, and shorter in both source and object form. EVERY > case was a triple win, with no loss. I have never found a case > where someone could provide me with a goto'ed code, and I could not > improve it in all three areas. Anyone reading this who sends me some Given even your rough example, you have not proven anything to me yet. > Goto lovers find it fairly simple to come up with code fragments that > require extra state variables or extra levels of procedure call to > improve. My removal of gotos sometimes required my return to the > functional specification of the system and a complete re-implementation. > (and yes, I re-implemented as little as necessary, so that this did > not skew the results too badly. I do believe that the re-implementation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ What does this mean? I could take this to mean the code is HARDER to read, has LONGER source, or just plain wrong. > >In any language that supports a complete set of structured constructs, >there is NO NEED for a goto, and the statement should be removed from >the language! > Well, Skip, you have not given me anything concrete to work on. Before you get all steamed up, let me say I agree with you *in principle* and I also strive quite hard to keep goto's out of the C code (and am over 95% successful, if I had to take a stab at the actual stats). However, there are some things I think you over-dramatized: (1) (Sub)programs without GOTOs are NOT always more readable. (2) Similarly, STRUCTURED code is not always easier to read. I have seen some pretty STRANGE structures which are "curable" (I know you hate the use here) by the addition of one GOTO. I have watched programmers beat themselves up on this one... (3) THERE IS *NO* GENERALIZATION THAT CAN BE MADE REGARDING CODE SIZE. I can give you HUNDREDS of examples (and bury your mail system :-) that clearly show a GOTO produces less code size, and I can give you a hundred more where STRUCTURE clearly produces the smaller code. It is dependent upon the function being implemented. (Please show me how a structure made more readable by moving a piece into a subfunction can produce SMALLER or FASTER code. At BEST, there is a CALL/RETURN opcode set and associated delay.) Again, I *agree* with the principle, but be realistic! I always strive to dump GOTOs in favor of structures REALIZING that code size may NOT go down, but that MAINTAINABILITY increases. I often "kick back" code which I feel has unnecessary GOTOs to the originator; since most of the time they are NOT needed. But to declare that GOTOs are NEVER acceptable is pure horse-puckey. (I am not sure the "grand master" was in favor of stamping them out altogether, I *believe* he felt the way I do - it is too easy to use GOTOs to get the code working rather than planning and structuring the program correctly - "GOTO disease" - and should only be used WHERE THE CODE WARRANTS IT.) There is a nice class of decision trees which can NEVER be implemented smaller in a structure and and appear MESSIER when in a structure (to me anyway) versus just a couple labels and GOTOs to "help the structure out". I would be happy to drop just one of these on the net if you have trouble believing me. -Mitch
rst@think.COM (Robert Thau) (07/18/87)
In article <7571@beta.UUCP> hwe@beta.UUCP (Skip Egdorf) writes: >In any language that supports a complete set of structured constructs, >there is NO NEED for a goto, and the statement should be removed from >the language! If you mean this literally, then we don't disagree, but I'm about to agree with you *very* heatedly. The only language I know well which supports a set of structured constructs complete enough that I can entirely avoid goto's is Common Lisp (salted with some reasonable way to handle errors, a serious omission from the standard. BTW, I've never used Ada). C does handle the common cases, but there are others... The example everyone knows is multi-level break. It's clearly structured, but C doesn't have it. (It isn't even possible to use 'break' to get out of a singly nested loop if the body is a switch statment). The obvious workaround is a 'goto' to a label at the end of the loop. The "pure" alternative is to add a bogus flag variable which is tested as part of the loop condition. I think this is actually *less* clear, since an assignment to the flag has the effect of altering flow control (by terminating the loop), but looks just like any other assigment statement. The 'goto' and label are at least explicit about what is really going on. (As a side comment, the test for the flag in the loop condition *will* produce slower code on every C compiler I have ever used. Few compilers for any language do data flow analysis sophisticated enough to detect that a variable is being used to cause a loop to terminate, replace assignments with branches, and *not* generate the test at the top of the loop. For time-critical inner loops, this can make a real difference). Error handling is another spot where goto's are often the lesser evil. Take the code: some_func() { lock_the_lock(); open_the_faucet(); while(...) { if ((buf = malloc(bufsize)) == NULL) goto error; if (readabuf(myfile, buf) == ERRORVALUE) goto error; ... process the buffer ... if (... the data was bogus ...) goto error; ... do some more ... } while (some_condition) prepare_the_report(); print_the_report(); error: close_the_faucet(); unlock_the_lock(); } There are two ways to eliminate the goto's that I'm aware of. One is to replace each "goto error" by { close_the_faucet(); unlock_the_lock(); return; } If the function is ever changed to open the refrigerator as well, all these sequences will have to be updated --- and ghod forbid one gets missed! The alternative is to replace each with: { errflag = true; break; } and surround the report-making code with an "if (!errflag)". This adds a bogus variable and obscures intent of the code behind an entirely gratuitous level of structure. Besides, {{ insert entire previous discussion of multi-level break }}. Note that in both these cases, the underlying problem is that C lacks a language feature (multi-level break; civilized error handling) and the programmer is forced to fake it. My claim is simply that faking it with goto's is better, all around, than faking it with flags. In almost all cases, goto-free code is simpler, clearer, and often faster and more compact. *Almost* all. Skip further argues that > ... As programmers modify code with gotos, lack of understanding > of the entire structure of the program leads to local modifications. > Additional jumps to local labels, and additions of code that span > earlier structures not visible due to the goto-implementation > cause Entropy increase, and the code becomes unstructured. I'm unconvinced. If the maintainers of a program can't resist the temptation to increase entropy, they'll screw it up, whether it originally had goto's or not. (Anyone charged with maintaining a program, at any time, can --- get this --- *insert* goto statements into code that had none of them to begin with!) The sad fact is that the *only* way to keep actively modified code from degenerating into sludge is ... > ... I began examining > lots of other code and re-doing it without gotos. Sometimes this was > hard, and involved a total re-design of the code (see below). ... slash-and-burn maintenance. If the original designer of the program didn't think that *that* function would *ever* return an error, or that the screen size would ever change in the *middle of a run*, or, or, or ..., then the code has to be rethought and, often, rewrit from scratch. Patching around this kind of situation will create a mess, but it's a worse mess if the code was unclear to begin with. And it *is an error* to mindlessly equate "clear" with "goto-free". > in EVERY case [of de-goto-izing] the code got more understandable, > more efficient, and shorter in both source and object form... but only because > ... My removal of gotos sometimes required my return to the > functional specification of the system and a complete re-implementation. Again, this is an argument for slash-and-burn maintenance, not an argument against the goto statement. (I do believe deeply in slash-and-burn maintenance, but that's another story). rst rst@think.com ihnp4!think!rst My employer would be shocked ...
henry@utzoo.UUCP (Henry Spencer) (07/19/87)
> My point is that 99% of the time I see a goto in a C program, it is > not there for any good reason... The rule I follow is that the desire to use a goto is a symptom of poorly- organized code, and in particular is a sign that code has not been split up into functions as it should be. Barring emergencies, the right response is to step back a bit and consider whether the code needs reorganizing. In twelve years of C programming, my experience has been that the answer *invariably* is "yes". I feel roughly the same way about "continue", by the way, although it's not as bad and I do use it occasionally. People who say "but I can't call functions, I can't afford the overhead" usually have no idea where the time in their program really is going, and thus have no rational basis for that statement. There may indeed be a few places where call overhead is unacceptable, but the time to do something about that is *after* the program is working and can be profiled. Then, and only then, it *may* be appropriate to revise a few small pieces of the code to be faster but a bit less clean. Inverting the order of these steps generally just makes an unnecessary mess. Human intuition simply is not a reliable guide to the location of the real bottlenecks. See also Collyer&Spencer, "News Need Not Be Slow", Washington 1987 Usenix conference proceedings, for more discussion of these issues in the context of improving news performance by 1.5 orders of magnitude. -- Support sustained spaceflight: fight | Henry Spencer @ U of Toronto Zoology the soi-disant "Planetary Society"! | {allegra,ihnp4,decvax,utai}!utzoo!henry
henry@utzoo.UUCP (Henry Spencer) (07/19/87)
> What this use of the goto boils down to is an exceptional return. > C doesn't have any kind of exception handling mechanism, which is a > part of any "complete set of structured constructs", so it is > necessary to simulate exception handling using goto. It is often possible, and usually more readable in my experience, to simulate it using return. This means splitting out the lump which wants an "exception exit" into a separate function, and just using return to punch out of it. Retrofitting this into existing code is a lot easier said than done, mind you -- the code has to be organized with this approach in mind. I am willing to concede that there may be programs which can't use this approach without serious problems; I've just never written one. -- Support sustained spaceflight: fight | Henry Spencer @ U of Toronto Zoology the soi-disant "Planetary Society"! | {allegra,ihnp4,decvax,utai}!utzoo!henry
henry@utzoo.UUCP (Henry Spencer) (07/19/87)
> There is a nice class of decision trees which can NEVER be implemented smaller > in a structure and and appear MESSIER when in a structure (to me anyway) > versus just a couple labels and GOTOs to "help the structure out"... Try putting it in a table-driven form before you reject goto-less solutions. Usually still more readable, seldom significantly slower if done carefully. Incidentally, I support a ten-year ban on use of the word "structured" to mean anything other than "organized" (which is, remember, its original meaning). If you mentally make this substitution, it sheds a whole new light on people who insist that "we can't use structured programming because it's too inefficient/clumsy/hard/whatever". -- Support sustained spaceflight: fight | Henry Spencer @ U of Toronto Zoology the soi-disant "Planetary Society"! | {allegra,ihnp4,decvax,utai}!utzoo!henry
hymie@dvm.UUCP (Hyman Rosen) (07/19/87)
In article <3289@bigburd.PRC.Unisys.COM> weiss@bigburd.PRC.Unisys.COM (Tom Weiss) writes: > >Since now appears to be the time to express opinions about >style/clarity, I would like to address my own pet peeve: the goto >statement. > >There are very few legitimate uses for the goto statement, and C >provides alternates for most of them. ... > ... >Once I was involved in rewriting a piece of code which contained a ... > ... goto [that] was used to jump >forward into the middle of an if/else construct. ... That's about the only time that I've had to use goto's. (I usually handle jumping out of a couple of loops by an extra test.) The case that particularly comes to mind is in converting a string to float, which I've had to do a number of times (in situations where atof() or sscanf() were not options). Handling the cases of no digits before the decimal point or no digits after the decimal point always seems to require either a duplication of code, or a branch into one of the cases of an if-else statement, and I always choose the goto. On the other hand, another programmer I work with insists that this is an abomination, and would outrightly ban jumps into blocks from C. Note that in C++ such branches are forbidden when they would cause a declaration of an item with a constructor to be bypassed. -- - Hymie ...{decvax,ihnp4,ucbvax}!allegra!phri!orville!dvm!hymie
g-rh@cca.CCA.COM (Richard Harter) (07/19/87)
In article <322@dvm.UUCP> hymie@dvm.UUCP (Hyman Rosen) writes: >> ... goto [that] was used to jump >>forward into the middle of an if/else construct. ... > >That's about the only time that I've had to use goto's. (I usually handle >jumping out of a couple of loops by an extra test.) The case that particularly >comes to mind is in converting a string to float, which I've had to do a >number of times (in situations where atof() or sscanf() were not options). >Handling the cases of no digits before the decimal point or no digits after >the decimal point always seems to require either a duplication of code, or >a branch into one of the cases of an if-else statement, and I always choose >the goto. On the other hand, another programmer I work with insists that this >is an abomination, and would outrightly ban jumps into blocks from C. Note >that in C++ such branches are forbidden when they would cause a declaration >of an item with a constructor to be bypassed. Different folks, different strokes. It's hard for me to think of a way to code this particular problem where you have a conceivable reason for wanting to jump into a if-else block. It might be interesting if you posted a sketch of the approach that occurs to you naturally. In theory I count myself among the defenders of the goto. In practice I use them very rarely if the language at hand has the constructs I need. For example in a 50,000 line PL/I program I used exactly one, to transfer out of an on-condition block. In a 50,000 line C program I used six, all of which were used to do cleanup in error handling. I avoid the need to escape from deep levels of nesting by never using deep levels of nesting. On the theoretical side, control constructs can be arranged in a heirarchy of strengths: 0 if-then-else/while 1 forever loop 2 simple goto 3 procedure call/return 4 computed goto Any program can be written using the level 0 constructs. Any program can be written using the level 0 constructs. Each level is stronger than the one below it. I.e. for any level we can find programs which will be more expensive in use of resources (space, time) if the constructs of that level are not used. This is one reason why no-goto advocates often say that one should use procedures to avoid code replication -- in effect, they are proposing that one should replace the weaker construct (the goto) by a stronger construct (the procedure call/return). Unfortunately the procedure call mechanism is not a panacea; the problems are (a) cost of invocation, and (b) transfer of information to the called procedure. Strikingly enough, the most powerful programming construct possible, the computed goto, is available in C (and most alternative languages) in the form of the switch/case construct. In theory, and in practice, problems with complex level 0 constructs can be resolved and simplified by recasting them in the form of switch/case constructs. I grant that the switch/case construct can be just as obscure as the code it replaces. Nonetheless it has been my experience that complicated logic problems are most conveniently handled by thinking of the problem in terms of a finite-state machine and a list of states and actions. -- Richard Harter, SMDS Inc. [Disclaimers not permitted by company policy.] [I set company policy.]
ron@topaz.rutgers.edu (Ron Natalie) (07/21/87)
> A large myth from the 70's, where structured programming was viewed > as "no gotos", was that forward jumps out of loops were not structured. > This is not true. A break transfers control to a known place and provides > just as structured a piece of code as a while leaving the loop on a false > condition. No, it is the break or jump to varying levels of loop exit that causes problems. Generally a good test for structure is to make each executable statement a point and draw lines indicating the possible paths from one point to another point. If the lines must cross, then you are losing structure (in the "everything is deeply intertwingled" terminology of DeMarco). -Ron
ron@topaz.rutgers.edu (Ron Natalie) (07/21/87)
> The example everyone knows is multi-level break. It's clearly > structured, but C doesn't have it. It is not clearly structured, it isn't structured at all. It destroys the loop structure. See my previous message > (It isn't even possible to use > 'break' to get out of a singly nested loop if the body is a switch > statment). This is a bug in C. "BREAK" was a bad choice, either switch should have implicitly assume break before the next case or it should have used a different reserve word. Witness the difference in operation of the word "continue" as a result. -Ron
karl@haddock.ISC.COM (Karl Heuer) (07/21/87)
In article <7571@beta.UUCP> hwe@beta.UUCP (Skip Egdorf) writes: >In any language that supports a complete set of structured constructs, >there is NO NEED for a goto, and the statement should be removed from >the language! The statement is true with the qualifier. However, I do not know any language that supports what I would consider a "complete set" of structured constructs. Perhaps ADA does, but I'm not sure I want to use something that big. >Languages that do not have enough control structure should be used only >with a pre-processor (e.g. Fortran and Ratfor). Not always feasible. Yes, if there's a Ratfor available I'll use it and avoid the goto as much as possible. But if I'm coding in C, it's unlikely that the construct I need occurs so often that it's worthwhile to use a new language. As for removing it from the language -- that has happened in the Bourne shell, and it's a pain having to kludge around it. (Still, it's better than the old "goto" shell.) For the record, I do not use goto very often. When I do, it's usually for error-handling (even then, I prefer "usage()" to "goto usage"). I've also been known to implement coroutines using goto. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
flaps@utcsri.UUCP (07/22/87)
In article <6603@think.UUCP> rst@godot.think.com.UUCP (Robert Thau) writes: >Error handling is another spot where goto's are often the lesser evil. >Take the code: > > some_func() > { > lock_the_lock(); open_the_faucet(); > > while(...) { > if ((buf = malloc(bufsize)) == NULL) goto error; > if (readabuf(myfile, buf) == ERRORVALUE) goto error; > ... process the buffer ... > if (... the data was bogus ...) goto error; > ... do some more ... > } > while (some_condition) prepare_the_report(); > print_the_report(); > error: > close_the_faucet(); unlock_the_lock(); > } > >There are two ways to eliminate the goto's that I'm aware of. [two not very appealing ways] Here's the nice way to eliminate the gotos. No extra charge for the improved vertical spacing. :-) some_func() { lock_the_lock(); open_the_faucet(); process_until_error(); close_the_faucet(); unlock_the_lock(); } process_until_error() { while(...) { if ((buf = malloc(bufsize)) == NULL) return; if (readabuf(myfile, buf) == ERRORVALUE) return; ... process the buffer ... if (... the data was bogus ...) return; ... do some more ... } while (some_condition) prepare_the_report(); print_the_report(); } -- // Alan J Rosenthal // \\ // flaps@csri.toronto.edu, {seismo!utai or utzoo}!utcsri!flaps, \// flaps@toronto on csnet, flaps at utorgpu on bitnet. "To be whole is to be part; true voyage is return."
mlinar@poisson.usc.edu (Mitch Mlinar) (07/22/87)
In article <8321@utzoo.UUCP> henry@utzoo.UUCP (Henry Spencer) writes: >> There is a nice class of decision trees which can NEVER be implemented smaller >> in a structure and and appear MESSIER when in a structure (to me anyway) >> versus just a couple labels and GOTOs to "help the structure out"... > >Try putting it in a table-driven form before you reject goto-less solutions. >Usually still more readable, seldom significantly slower if done carefully. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Yes, a table-driven form is goto-free, but I am not sure it is *really* readable. (I admit it is probably more readable than the goto solution.) The main point I was trying to make (and supported by your statement), is that the code is NOT always faster when done right. Personally, I prefer code that is maintainable, even if there is some speed loss. (To say that correct code is ALWAYS faster and/or smaller is a fallacy.) -Mitch
dg@wrs.UUCP (David Goodenough) (07/22/87)
In article <765@haddock.ISC.COM> karl@haddock.ISC.COM (Karl Heuer) writes: >In article <7571@beta.UUCP> hwe@beta.UUCP (Skip Egdorf) writes: >>In any language that supports a complete set of structured constructs, >>there is NO NEED for a goto, and the statement should be removed from >>the language! > >The statement is true with the qualifier. However, I do not know any language >that supports what I would consider a "complete set" of structured constructs. >Perhaps ADA does, but I'm not sure I want to use something that big. Now it's my turn to play devil's advocate - I seem to remember that in some obscure journal or other, one of those structured programming gurus (i.e. Djykstra (sp??) / Wirth) said that the only structured concepts needed are loops and ifs, everything else is just icing. Now looking at every language so far designed, I see some form of loop and some form of if. So what are we (myself included) all bitching about?????????? -- dg@wrs.UUCP - David Goodenough +---+ | +-+-+ +-+-+ | +---+
mwm@eris.BERKELEY.EDU (Mike (My watch has windows) Meyer) (07/23/87)
In article <8320@utzoo.UUCP> henry@utzoo.UUCP (Henry Spencer) writes:
<It is often possible, and usually more readable in my experience, to
<simulate it using return. This means splitting out the lump which
<wants an "exception exit" into a separate function, and just using
<return to punch out of it. Retrofitting this into existing code is a
<lot easier said than done, mind you -- the code has to be organized
<with this approach in mind.
<
<I am willing to concede that there may be programs which can't use this
<approach without serious problems; I've just never written one.
Depends on what you're willing to do to the function.
I just wrote a short utility (couple of hundred lines) that had as
many gotos as I've actually left in code in the last 10 years. In both
cases, I wrote the code without the goto, said "yuch", and rewrote it
with the obvious goto/label sets.
One case was just what you describe: needing a short-circuit exit from
a piece of code. The piece of code parsed a string, extracting six
integer values from it, and leaving the pointer to the string in a
specific location. I considered writing this as a function and using
the return for short-circuit, and got as far as writing (the compiler
in question support prototypes):
char *parse_geometry(char *, int *, int *, int *, int *, int *, int *) ;
before deciding that this was going to be *ugly*. Wrapping those six
values up in a structure would have led to:
six assignments to the members of geometry_data ;
input_string = parse_geometry(input_string, &geometry_data) ;
six assignments to extract the data from geometry_data ;
Not very much prettier.
If any of you who claim that there's *never* an excuse for a goto want
to look at this, and show me how to do it without goto's, I'd
appreciate it. I don't like gotos - I just couldn't see a more
readable way of doing things with this code. But be warned - it's not
a Unix utility.
<mike
--
Must have walked those streets for hours, Mike Meyer
In the dark and in the cold, mwm@berkeley.edu
Before I really could accept, ucbvax!mwm
There's no place called hope road. mwm@ucbjade.BITNET
mikec@tekred.TEK.COM (Mike Combs) (07/23/87)
In article <7571@beta.UUCP> you write: ]In article <3289@bigburd.PRC.Unisys.COM>, weiss@bigburd.PRC.Unisys.COM (Tom Weiss) writes: ]> Since C provides the break statement to break out of a loop, the ]> continue statement to jump back to the top of a loop, the return ]> statement to immediately exit a function, and access to system calls ]> (e.g. exit) that can terminate execution in error handling functions, ]> there are few 'legitimate' uses for goto. ] ]In any language that supports a complete set of structured constructs, ]there is NO NEED for a goto, and the statement should be removed from ]the language! ]Languages that do not have enough control structure should be used only ]with a pre-processor (e.g. Fortran and Ratfor). ] ] Skip Egdorf ] hwe@lanl.gov What would you say a "complete set of structured constructs" includes? Does Pascal have a complete set? I'm a longtime Pascal programmer, and just starting to learn C. Pascal does not have the break, continue, or return statements. In your opinion, in the cases Weiss refers to, does this justify use of goto? -- Mike A. Combs ^--The "A" is for: "Almost finished debugging." GEnie: mike.combs MCI: mcombs tektronix!tekgen!tekred!mikec terrorist, contras, drugs, Iran, secret, NSA, CIA <- NSA line-eater food :-( -- Mike A. Combs ^--The "A" is for: "Almost finished debugging." GEnie: mike.combs MCI: mcombs tektronix!tekgen!tekred!mikec terrorist, contras, drugs, Iran, secret, NSA, CIA <- NSA line-eater food :-(
aglew@ccvaxa.UUCP (07/23/87)
Here's a question of taste: When you've completed an application nicely in C, using functions everywhere, and it's still too slow, which would you rather do?: (1) Rewrite parts of it in assembler, or (2) Use gotos?
jfh@killer.UUCP (The Beach Bum) (07/23/87)
In article <3567@oberon.USC.EDU>, mlinar@poisson.usc.edu (Mitch Mlinar) writes: > In article <8321@utzoo.UUCP> henry@utzoo.UUCP (Henry Spencer) writes: > >> There is a nice class of decision trees which can NEVER be implemented smaller > >> in a structure and and appear MESSIER when in a structure (to me anyway) > >> versus just a couple labels and GOTOs to "help the structure out"... > > > >Try putting it in a table-driven form before you reject goto-less solutions. > >Usually still more readable, seldom significantly slower if done carefully. > ^^^^^^^^^^^^^^^^^^^^^^^^^^^ > Yes, a table-driven form is goto-free, but I am not sure it is *really* > readable. (I admit it is probably more readable than the goto solution.) > > The main point I was trying to make (and supported by your statement), > is that the code is NOT always faster when done right. Personally, I prefer > code that is maintainable, even if there is some speed loss. (To say that > correct code is ALWAYS faster and/or smaller is a fallacy.) > > -Mitch I find when writing programs as finite state machines, that the code is somewhat less readable _but_ almost always infinitely more correct. Usually some big switch statement and a table of state changes (anyone who has written a push down automaton knows just how big these tables can get for a grammar like C or pascal ...) does the trick. The actual code is so simple and the tables can be generated almost automatically (unless you use yacc, in which case it can be generated automatically :-) That, in this humble programmer's opinion, is the highest form of structure. A couple dozen lines of code, and an array with a couple thousand entries ... The points I am trying to make are: 1). Readablity affects maintainablity. 2). Structure affects maintainablity. 3). Design affects maintainablity. I _always_ document my goto's (about 1 or 2 percent of my statements) with a `come from' that describes the state and condition of the program when execution reaches that point in the code. This does two things: 1). Improves the readablity. 2). (generally) Improves the design. The second is a result of having to think of what conditions you want flow control to reach the goto. Consider the following fragment: (Keep in mind that I would never actually write this piece of code ...) OpenFile (FileName, FilePointer, FileBuffer, RecordSize) char *FileName; FILE **FilePointer; char **FileBuffer; int RecordSize; { *FilePointer = NULL; *FileBuffer = NULL; if (access (FileName, 0) == -1) goto ErrorExit; if ((*FilePointer = fopen (FileName, "r")) == NULL) goto ErrorExit; if ((*FileBuffer = malloc (RecordSize)) == NULL) goto ErrorExit; /* Maybe some more processing, and jumps to ErrorExit */ return (1); /* ErrorExit - handle all known forms of errors ... * You know how you got here? Right? You know what to do? Right? */ ErrorExit: if (*FilePointer) fclose (*FilePointer); if (*FileBuffer) fclose (*FileBuffer); return (0); } Any questions? This may not be `structured', ala University Pascal style structured, but I'd rather see this than some nasty collection of deeply nested if-then-else-if-...'s or 3 or 4 `error handlers' that look just like the code in ErrorExit with a few minor changes. - John. -- John F. Haugh II HECI Exploration Co. Inc. UUCP: ...!ihnp4!killer!jfh 11910 Greenville Ave, Suite 600 "Don't Have an Oil Well?" Dallas, TX. 75243 " ... Then Buy One!" (214) 231-0993
dave@sdeggo.UUCP (David L. Smith) (07/23/87)
In article <264@wrs.UUCP>, dg@wrs.UUCP (David Goodenough) writes:
- Now it's my turn to play devil's advocate - I seem to remember that in
- some obscure journal or other, one of those structured programming gurus
- (i.e. Djykstra (sp??) / Wirth) said that the only structured concepts
- needed are loops and ifs, everything else is just icing. Now looking at
- every language so far designed, I see some form of loop and some form
- of if. So what are we (myself included) all bitching about??????????
- --
- dg@wrs.UUCP - David Goodenough
All you need to travel across the United States is yor bare feet but it's
a whole lot nicer in a plane :-)
--
David L. Smith
{sdcsvax!sdamos,ihnp4!jack!man, hp-sdd!crash}!sdeggo!dave
sdeggo!dave@sdamos.ucsd.edu
Microport Unix! More bugs for the buck!
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/24/87)
In article <264@wrs.UUCP> dg@wrs.UUCP (David Goodenough) writes: >I seem to remember that in >some obscure journal or other, one of those structured programming gurus >(i.e. Djykstra (sp??) / Wirth) said that the only structured concepts >needed are loops and ifs, everything else is just icing. That's not quite what the Bohm-Jacopini theorem says. It says that any procedural logic (flowchart) can be derived from combinations of three basic elements: sequence, if-then-else, and do-while. But that does not have as much relevance to the discussion as you might think. Consider that propositional calculus (Boolean logic) needs only one operator (your choice of NAND or NOR); nevertheless it is much more in line with human modes of thinking (at least in the civilized Western world) to express such logic in terms of several different operators (AND, OR, NOT, IMPLIES, ...). This is not "incorrect", and from a practical point of view it is MORE correct than insisting on writing all Boolean operations in terms of NANDs. Similarly, just because one CAN write a program using a tiny number of constructs does not at all mean that one SHOULD do so. It is more important that the program be patently correct (i.e., that it exactly implement the design function) and that it be susceptible to ready understanding and modification without losing those nice properties. Discussion about whether or not to use "goto" is just about as pointless as discussing whether or not to call a variable, "event"; if it makes sense to do so in the context of producing correct, maintainable code, one will naturally do so without having to stop to debate the issue. I don't often use "goto" in my C code, but there are times when it is simply the most obviously correct way to implement a procedure. One COULD avoid using "goto" but why unnecessarily convolute the code? Now, I'm sure we've all seen horrendous abuses of "goto". But ALL programming constructs can be and have been badly abused. I recall one posted use of "switch" that was admirably ingenious and gruesome! (Sorry, I don't recall the details.)
cik@l.cc.purdue.edu (Herman Rubin) (07/24/87)
I have actual subroutines which, inside the main loop, have code such as ..... mod: ..... ..... if(....)goto terma; .... if(...)goto termb; ... if(......)goto termc; h: .... if(.....)goto terma; .... goto h; terma: ......; goto mod; termb; ....; goto merge; termc; ....; merge: ....; goto finish; termd: ....; finish: ....;} as well as other goto's internally in the code. The goto h could be replaced by a while() statement, but would be no more readable. I see no remotely reasonable way to replace the other goto's without both slowing the code and decreasing readability. I know goto's can be dangerous, but I do not call a tank to drive me to work. As far as proving the correctness of a program, this program is actually fairly short, and each part is implementing a case in a rather intricate procedure which does a lot of branching from case to case until a termination condition is attained. If the C compiler on the VAX could be persuaded to use all the registers, no non-explicit memory references would be used. Clarity of code is not achieved by not allowing the programmer to do what is natural, and there is nothing unnatural about goto's except in the minds of the ignorant or totalitarian :-). -- Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Phone: (317)494-6054 hrubin@l.cc.purdue.edu or pur-ee!stat-l!cik or hrubin@purccvm.bitnet
lmiller@venera.isi.edu (Larry Miller) (07/24/87)
In article <264@wrs.UUCP> dg@wrs.UUCP (David Goodenough) writes: >>Perhaps ADA does, but I'm not sure I want to use something that big. > >Now it's my turn to play devil's advocate - I seem to remember that in >some obscure journal or other, one of those structured programming gurus >(i.e. Djykstra (sp??) / Wirth) said that the only structured concepts >needed are loops and ifs, everything else is just icing. Now looking at >every language so far designed, I see some form of loop and some form >of if. So what are we (myself included) all bitching about?????????? >-- The paper was (refer format): %A C. Boehm %A G Jacopini %T Flow Diagrams, Turing Machines, and Languages With Only Two Formation Rules %J C. ACM %D May, 1966
grazier@fmsrl7.UUCP (Kevin Grazier) (07/24/87)
In article <7571@beta.UUCP> hwe@beta.UUCP (Skip Egdorf) writes: >In any language that supports a complete set of structured constructs, >there is NO NEED for a goto, and the statement should be removed from >the language! For what it's worth, I don't entirely agree. Take, for instance, code which is time-critical (control systems software, for example). Now, add nested switch statements. When the code is executed at the "lowest level" of the switch, it's so much more practical to jump to the end of the swithces instead of executing the break break break.......ad infinitum sequence. When you're running real-time code which is running something like an engine, you look for speed ANYWHERE. This is another case in which practicality diverges slightly from the theoretical. On a more intangible level, I seem to remember being taught as an undergrad that there are still a COUPLE (and I mean just that) algorithms that can't be done without a goto. Unfortunately, I can't remember what they did, though. If this receives too many flames, maybe I'll have to look it up. What the instructor was trying to show by this example, though, was that there are, indeed, only a COUPLE algorithms which cannot be done without gotos and that since we weren't doing anything near as complex, we shouldn't use them (actually couldn't use would be more proper because if you used one, you flunked the assignment). -- Kevin R. Grazier Have you driven a Ford, lately? Ford Motor Company Scientific Research Labs Advanced Powertrain Systems & Controls Engineering uucp: {philabs | pyramid} !fmsrl7!grazier OR grazier@fmsrl7.UUCP VOICE: (313) 739-8586
asg@pyuxf.UUCP (alan geller) (07/24/87)
In article <1237@ius2.cs.cmu.edu>, edw@ius2.cs.cmu.edu.UUCP writes: > > In an article by Skip Egdorf (hwe@beta.UUCP), Skip sez, > > There are NO legitimate uses for the goto statement... > > I sez.. > There is a whole class of problems that map very nicely into goto contructs. > They are simulation of NFAs and DFAs (ie finite state machines). > > States map very nicely to labels and transitions map very nicely into > if (input == ?) goto label. > > The most readable way one can represent the NFA/DFA is through a mesh > of gotos with a diagram of the machine in comments :-). I disagree -- the most readable way to present a finite state machine is with a transition matrix (array), an action matrix (array), and a driver routine that looks like: state = INITIAL_STATE; while (state != ACCEPT_STATE) { input = getInput(); performAction(actionMatrix[state][input]); state = transitionMatrix[state][input]; } or something along those lines. This produces code that is more easily maintained, more compact, and often faster than using a bushel of goto's. Alan Geller {rutgers,princeton}!pyuxp!pyuxf!asg Bellcore, 33 Knightsbridge Rd., Piscataway, N.J. (201)-699-7641 My employers had nothing to do with the above -- unless it's patentable :-).
rwhite@nu3b2.UUCP (Robert C. White Jr.) (07/25/87)
In all your goto stuff I find that yhat you really want is the setjump() longjump() construct whic has been in all [both might be a better word, its been marginaly over three] the compilers I have had to work with. It would seem to address all your examples of necessary goto(s). You call setjump() and get a return code indicating the jump has been set to that point. If you later call longjump() execution skips BACK to the setjump() call but this time it exits with a diferent value you can test. This only works backwards as it mostly saves the stack frame in some global position. but it will preform a multi level "break" as long as you are on the same or lower execution branch as the setjump() call. The conditional structure seems valid enough, and as it is really a controlled, down-only, step is seems a terrific structual behavor for a language which is basicly a "portable asssembeler" [as some have called it] It is exxentially the same as a multi-level break, but it requires the structual planning to be done at the broken-to, instead of broken- from, level. by this means, extra levels [functions/loops/falderall] can be inserted in the execution tree without having to find every "break 4" and change it to a "break 5" [etc], the programmer must plan a structured response to every possible goto. Robert. Disclaimer: My mind is so fragmented by random excursions into a wilderness of abstractions and incipient ideas that the practical purposes of the moment are often submerged in my consciousness and I don't know what I'm doing. [my employers certainly have no idea]
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/25/87)
In article <556@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
- .....
-mod: .....
- .....
- if(....)goto terma;
- ....
- if(...)goto termb;
- ...
- if(......)goto termc;
-h: ....
- if(.....)goto terma;
- ....
- goto h;
-terma: ......;
- goto mod;
-termb; ....;
- goto merge;
-termc; ....;
-merge: ....; goto finish;
-termd: ....;
-finish: ....;}
-as well as other goto's internally in the code.
That's an example of the sort of thing that gave GOTO a bad name.
If you draw connecting paths between the gotos and their targets,
you get the infamous "spaghetti". It is always POSSIBLE that such
code is correct, but it is not patent from its structure. Indeed,
it's not even clear that the branching necessarily terminates, and
the termination condition if written out would be exceedingly
complex. By the way, how is label "termd" reached? Are you sure
this code is correct? Would you be comfortable making serious
revisions to it a few years from now? I really can't recommend
this style of coding.
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/26/87)
In article <1125@nu3b2.UUCP> rwhite@nu3b2.UUCP (Robert C. White Jr.) writes: >In all your goto stuff I find that yhat you really want is the >setjump() longjump() construct... NO! longjmp() is for emergency use only; it suffers from several problems: (1) It's a non-local goto, which is even worse than a local goto from the standpoint of style. It is also more complex because of the implied conditional test. (2) setjmp()/longjmp() have to save and restore considerable context and usually involve run-time library function calls, so it is much less efficient than goto. (3) It is sometimes very hard to implement setjmp()/longjmp() in such a way that something doesn't get messed up at run time, usually by reentrancy from a signal handler or at least corruption of register variables. I don't recommend setjmp()/longjmp() at all, but if you need to leap out from the middle of deeply nested function invocations to the "top" of a master control loop, they are useful for that. Beware of longjmp() from a signal handler, though; no telling what corruption of data structures that may cause.
cik@l.cc.purdue.edu (Herman Rubin) (07/26/87)
In article <6172@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: > In article <556@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes: [details omitted] > That's an example of the sort of thing that gave GOTO a bad name. > If you draw connecting paths between the gotos and their targets, > you get the infamous "spaghetti". It is always POSSIBLE that such > code is correct, but it is not patent from its structure. Indeed, > it's not even clear that the branching necessarily terminates, and > the termination condition if written out would be exceedingly > complex. By the way, how is label "termd" reached? Are you sure > this code is correct? Would you be comfortable making serious > revisions to it a few years from now? I really can't recommend > this style of coding. First, I gave only a disjointed fragment of the code, not enough to produce the flow. Second, there is random input at all stages of the code before reaching a terminating condition, and I can only state that the code terminates with probability one, but the expected termination time is short. Third, I doubt that I would be able to prove the correctness of the algorithm without knowing what the purpose of each section is; however, knowing this, it is not difficult. The procedure is a modification of a more easily described algorithm, but using the minimum amount of random information to carry out each stage--only isolated random bits are used. Fourth, it would be folly to attempt to revise this code without that description. If you think you can produce better code with or without the use of goto's, please let me know and I will give you the opportunity, but note that by better I mean faster. -- Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Phone: (317)494-6054 hrubin@l.cc.purdue.edu or pur-ee!stat-l!cik or hrubin@purccvm.bitnet
ebh@cord.UUCP (Ed Horch) (07/26/87)
In an article by Skip Egdorf (hwe@beta.UUCP), Skip sez,
> There are NO legitimate uses for the goto statement...
What about the K&R example
if( disaster )
goto bailout;
-Ed
jfh@killer.UUCP (The Beach Bum) (07/26/87)
In article <264@wrs.UUCP>, dg@wrs.UUCP (David Goodenough) writes: > In article <765@haddock.ISC.COM> karl@haddock.ISC.COM (Karl Heuer) writes: > >In article <7571@beta.UUCP> hwe@beta.UUCP (Skip Egdorf) writes: > >>In any language that supports a complete set of structured constructs, > >>there is NO NEED for a goto, and the statement should be removed from > >>the language! > > > >The statement is true with the qualifier. However, I do not know any language > >that supports what I would consider a "complete set" of structured constructs. > >Perhaps ADA does, but I'm not sure I want to use something that big. > > Now it's my turn to play devil's advocate - I seem to remember that in > some obscure journal or other, one of those structured programming gurus > (i.e. Djykstra (sp??) / Wirth) said that the only structured concepts > needed are loops and ifs, everything else is just icing. Now looking at > every language so far designed, I see some form of loop and some form > of if. So what are we (myself included) all bitching about?????????? Every high level construct can be faked with just if's and goto's. But even if's can be faked with top-tested loops. (while loops in c and pascal, etc) Consider: if boolean_expression then statement; and loop_ending_flag := false; while boolean_express and not loop_ending_flag do begin statement; loop_ending_flag := true end both of these are the same! Only the second _probably_ runs slower and definitely looks like crap. The second example is for wanting a loop and no goto's: while_loop: if boolean_expression then begin statement; goto while_loop end; and while boolean_expression do statement; Once again, both of these should be the same, except the second is now much more clear. Even a case for the case statement can be made with multi-level if's or some other form of computed goto. The variety of language contructs in a language such as pascal or c makes for _more_clearly_ expressing the intent of the programmer. I am against any group of people telling me not to use a language construct. - john. -- John F. Haugh II HECI Exploration Co. Inc. UUCP: ...!ihnp4!killer!jfh 11910 Greenville Ave, Suite 600 "Don't Have an Oil Well?" Dallas, TX. 75243 " ... Then Buy One!" (214) 231-0993
edw@ius1.cs.cmu.edu (Eddie Wyatt) (07/27/87)
In article <960@fmsrl7.UUCP>, grazier@fmsrl7.UUCP (Kevin Grazier) writes: > sequence. When you're running real-time code which is running something like > an engine, you look for speed ANYWHERE. This is another case in which > practicality diverges slightly from the theoretical. Buy yourself one hell of an optimizing compiler or write it in assembly if your code is that time critial. > > On a more intangible level, I seem to remember being taught as an undergrad > that there are still a COUPLE (and I mean just that) algorithms that > can't be done without a goto. Unfortunately, I can't remember what they > did, though. If this receives too many flames, maybe I'll have to look > it up. What the instructor was trying to show by this example, though, was > that there are, indeed, only a COUPLE algorithms which cannot be done > without gotos and that since we weren't doing anything near as complex, > we shouldn't use them (actually couldn't use would be more proper because > if you used one, you flunked the assignment). > Sorry, but ALL computable problems can be coded with ONLY while statements and if-then-elses for flow of control. In fact I'll go one step farther, ALL computable problems can be coded with only ONE while statement and if-then-elses. Every Von Newman machine is an example of this. while (1) fetch instructure /* can be viewed as an array index */ decode instructure /* can be decoded with only if-then-else think about */ execute instruction /* goto statements are actually just set PC = label */ inc PC I sugguest you pick up a book on Automata Theory and formal languages. The actually theorem that states if gotos are over kill was derived in the 30's by Kleene. I also sugguest you look up Church-Turning Thesis. -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
edw@ius1.cs.cmu.edu (Eddie Wyatt) (07/27/87)
In article <162@pyuxf.UUCP>, asg@pyuxf.UUCP (alan geller) writes: > In article <1237@ius2.cs.cmu.edu>, edw@ius2.cs.cmu.edu.UUCP writes: > > > > In an article by Skip Egdorf (hwe@beta.UUCP), Skip sez, > > > There are NO legitimate uses for the goto statement... > > > > I sez.. > > There is a whole class of problems that map very nicely into goto contructs. > > They are simulation of NFAs and DFAs (ie finite state machines). > > > > States map very nicely to labels and transitions map very nicely into > > if (input == ?) goto label. > > > > The most readable way one can represent the NFA/DFA is through a mesh > > of gotos with a diagram of the machine in comments :-). > > I disagree -- the most readable way to present a finite state machine > is with a transition matrix (array), an action matrix (array), and a > driver routine that looks like: > > state = INITIAL_STATE; > while (state != ACCEPT_STATE) { > input = getInput(); > performAction(actionMatrix[state][input]); > state = transitionMatrix[state][input]; > } > or something along those lines. This produces code that is more easily > maintained, more compact, and often faster than using a bushel of goto's. START : input = getchar(); if (isdigit(input) goto INTEGER; if (input == '.') goto FLOAT ; if (input == '+') goto START ; if (input == '-') goto START ; goto ERROR; INTEGER : input = getchar(); if (isdigit(input) goto INTEGER; if (input == '.') goto FLOAT2; ungetc(stdin,input); return(INT_TOKEN); FLOAT : input = getchar(); if (isdigit(input) goto FLOAT2; goto ERROR; FLOAT2 : input = getchar(); if (isdigit(input) goto FLOAT2; ungetc(stdin,input); return(FLOAT_TOKEN); ERROR : (void) fprintf(stderr,"lex error\n"); goto START; /* try again */ Is the above code really that hard to understand? BTW I know there are much more efficient ways of coding the above - this is just an example. I would say that transitional tables are no more readable than this approach, but I guess this is all a matter of taste. Also, the transitional tables have one very good advance over this approach which is, it requires the programmer to enumerate all posible actions associated with every state-input pair. But transitional tables are wasteful if the matrix is sparse (ie. more error transitions than good transitions). -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.eduo
edw@ius1.cs.cmu.edu (Eddie Wyatt) (07/28/87)
As someone kindly pointed out read(fileno(stdout),&c,sizeof(char)) should have been read(fileno(stdin),&c,sizeof(char)) ^^ -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
schwartz@swatsun (Scott Schwartz) (07/29/87)
In article <28700017@ccvaxa>, aglew@ccvaxa.UUCP writes: +------- | When you've completed an application nicely in C, | using functions everywhere, and it's still too slow, | which would you rather do?: | | (1) Rewrite parts of it in assembler, | or (2) Use gotos? +------- I would profile it and rewrite parts in assembler. We've all heard that line before, so no one should be surprised that it turns out to be the case. I can't think of any sizable program that would exibit a _dramatic_ speed increase when you ripped out switches and if-then-else's to put in goto's. The only case where this seems to be any kind of issue is in state machines and parsers; there has been enough discussion both ways on this recently to convince me that goto's are not necessarily the best way to do it in every case. I happen to like the "ErrorExit" usage that was talked about in several prevous postings, but I disagree with the insinuation above that functions are too slow to be practical. Two experiences come to mind: The first case was an egrep style program I once wrote, the second was a ray tracing program a friend of mine wrote. In both cases I wanted to speed things up without doing rewrites in assembler. So I tried replacing some of the functions in the inner loops with inline expansions [naturally a good optimizing compiler would have done that for me, but...]. The functions I chose to replace were identified (using unix profiling tools) and were called over 300,000 times in the test run. Replacing with inline code made virtually no difference in execution time. What I concluded from this exercise was that quick fixes usually don't work, and that in many cases function calls are less expensive than you think, especially if you have a naive compiler. -- # Scott Schwartz # ...{{seismo,ihnp4}!bpa,cbmvax!vu-vlsi,sun!liberty}!swatsun!schwartz
rwhite@nu3b2.UUCP (Robert C. White Jr.) (07/29/87)
In article <1191@killer.UUCP>, jfh@killer.UUCP (The Beach Bum) writes: > > Every high level construct can be faked with just if's and goto's. [Lots of examples of end-tested loops and such] > The whole point is not that people should "fake" high level constructs with goto(s) and if(s). If you think a moment, you will realize that vary few [any?] computers have a machine level "while" construct that can span multiple instructions. When push comes to shove ALL high leve constructs are reduced to tests and branches (if(s) and goto(s)) But it is silly bordering on foolish to maintain that using if and goto to build a loop will net more efficient code. Optimizing compilers can only optimize if they can group code statments and analize the possible execution paths. By spotting obviously (needlessley) repetitive code and moving it outside the/a loop in question: i.e. int func(x); int *x; { . . . while (test-which-does-not-use-x-as-value) { y = x+4; x+4 = z; [loop-body-which-does-not-modify-x] } . . . } In this example the x+4 would be moved out of the loop to increase efficency. If I did this loop with gotos, the compiler would not know where "out of the loop" was, so the calculation would be kept in every instance. Also an if thends to generate: test jump forward on false to X true instructions jump forward towards exit to Y label X false instructions label Y If you seround this with the loop branching, and the add the branch out as the "true instructions" for a negated test [just ignore the "else" branch sequence] you end up branching twice for each condition. the extra branch instructions will flush any instruction prefech queus and generally slow the system down an extra time. Between the extra branching and the loss of optomizing the code generated by goto programming is almost always slower than the loops. inline testing for a nonzero value [C boolean tests] are usually inline zero-extra-branch tests which can slip into the stream in such a way that no prefetch queues are disrupted and no extra branches take place. To use test-break logic you must branch excessively around the false contidtions. on an 80x86 machine or 80x88 machine a bolean test is: mov ax,0 test ax,[address of value] je instruction-after-loop ; /* jump if equal, out of loop As long as the boolean is true, the jump is ignored. After optomizing multiple tests, only the last two are repeated. This is VERY efficent code. if you use the test and break form the end I garentee at least one executed branch per test. Claiming a boolean test in a while loop's conditional expression will slow code is stupid. Get you your DDT [Dynamic Debugging Tool] of choice and examine the code. Using GOTO is a BOG. Robert. [I know I can't spell, if you don't like my words, but all you answer is the spelling it goes to >/dev/null, ALL other critiques taken seriously.] nu3b2!rwhite
mike@arizona.edu (Mike Coffin) (07/30/87)
In article <1129@nu3b2.UUCP> rwhite@nu3b2.UUCP (Robert C. White Jr.) writes: > ... > Optimizing compilers can only optimize if they can group >code statments and analize the possible execution paths. By spotting >obviously (needlessley) repetitive code and moving it outside the/a >loop in question: [ example containing a while loop omitted ] > > In this example the x+4 would be moved out of the loop to >increase efficency. If I did this loop with gotos, the compiler >would not know where "out of the loop" was, so the calculation would >be kept in every instance. ... This is incorrect. It is not at all difficult for a compiler to detect loops that are built from gotos instead of higher level constructs. Any decent compiler book has algorithms for doing it. In fact, most optimizing compilers convert source code into some intermediate form before reorganizing it; in the process of converting to intermediate code the higher-level constructs (with the exception of switch/case-like statements) are turned into ifs and gotos. If you want to argue against gotos, I think you'll have to do it on the basis of readability. mike coffin (mike@arizona.edu)
rcw@qetzal.UUCP (sysop) (08/02/87)
In article <1125@nu3b2.UUCP>, rwhite@nu3b2.UUCP (Robert C. White Jr.) writes: [The Robert C. White, Jr. quoted is not me. I am the other one] > > In all your goto stuff I find that what you really want is the > setjump() longjump() construct which has been in all [both might > be a better word, its been marginally over three] the compilers > I have had to work with. It would seem to address all your examples > of necessary goto(s). > I agree! We have used setjump() and longjump() to develop an error system for software development in-house. This allows us 1. To set an error control point. 2. To Remove an error control point. 3. Return to previous error control point. 4. Handle cleanup. 5. Eliminate a lot of code like: if (status == BARF) barfflag++; This sort of code usually constitutes > 1/3 the project. 6. Allow error logging in a journal. We have created quite a few utility routines which eliminate the need for the programmer to constantly check status. For example, we have a routine U_open, which is an interface to open(). fd = U_open("Foo",O_RDWR,0666); The U_open routine issues the appropriate message to the user's terminal if there is a problem. You don't have to check errno or fd yourself, just open it and away you go. Unfortunately, our code is proprietary, but I can hitch you up with an excellent consultant who can provide an excellent error system for you. -- Robert C. White, Jr. Graphics Information, Inc. **************** UUCP: ihnp4!upba!qetzal!rcw isis!qetzal!rcw * OIL/GAS/LAND * USPS: 3067 Robin Way, Denver, CO 80222 * CARTOGRAPHY * ATT : +1 303 759-3666 ****************
henry@utzoo.UUCP (Henry Spencer) (08/03/87)
> When you've completed an application nicely in C, > using functions everywhere, and it's still too slow, > which would you rather do?: > > (1) Rewrite parts of it in assembler, > > or (2) Use gotos? Neither. First, you *profile* it, to find out where the time is really going; human guessing (even if dignified by names like "intuition" or "professional judgement") is usually wrong. Then, you revise the code in the hot spots to use better algorithms. If you are really greasing the code hard, it may be necessary to merge previously-separate functions into their callers, to reduce call overhead. The big wins come from reorganization to avoid work entirely, not from twiddling to do the work a little bit faster. For example, C News is now 1.5 orders of magnitude faster than B News, and will probably be still faster when Geoff and I release it, even though it contains no gotos and no assembler. We both dislike gotos, and avoid assembler on the grounds that it's unnecessary and a maintenance nightmare for portable code. (There ARE occasions when small assembler functions can be a win, if you are trying to perform operations that C isn't too good at, but often a radically new algorithm -- in C -- is still faster.) Geoff does a lot of profiling and code analysis and rewriting (I do some too, but his parts of the code are the hardest and most speed-critical ones), and we both think a lot about ways to avoid overhead. For the gory details, see our paper in the Winter 1987 Usenix proceedings. We may do a followup paper someday about all the things we've thought of since. -- Support sustained spaceflight: fight | Henry Spencer @ U of Toronto Zoology the soi-disant "Planetary Society"! | {allegra,ihnp4,decvax,utai}!utzoo!henry
henry@utzoo.UUCP (Henry Spencer) (08/03/87)
> Also, the transitional tables have one very good advance over this approach > which is, it requires the programmer to enumerate all posible actions > associated with every state-input pair... I'd like to second this comment; I had a recent experience with such a situation. That code would *never* have been right if I hadn't converted it to a table-driven approach, which forced an organized, careful analysis of each and every case rather than "add code until it seems to work". -- Support sustained spaceflight: fight | Henry Spencer @ U of Toronto Zoology the soi-disant "Planetary Society"! | {allegra,ihnp4,decvax,utai}!utzoo!henry
peter@sugar.UUCP (Peter da Silva) (08/04/87)
Was that posting just a fragment of your imagination? You're actually suggesting that you replace a non-structured construct with one that is no better structured, requires more work on the part of the programmer, and executes slower? I though I heard you suggesting that: if(...) { while(...) { for(...) { .... goto loopexit; } } } loopexit: be replaced by: struct jmp_buf loopexit; if(!setjmp(&jmp_buf)) { if(...) { while(...) { for(...) { .... longjmp(&jmp_buf, 1); } } } } -- -- Peter da Silva `-_-' ...!seismo!soma!uhnix1!sugar!peter (I said, NO PHOTOS!)
roger@esquire.UUCP (Roger Reid) (08/04/87)
In article <28700017@ccvaxa> aglew@ccvaxa.UUCP writes: > >Here's a question of taste: > > When you've completed an application nicely in C, > using functions everywhere, and it's still too slow, > which would you rather do?: > > (1) Rewrite parts of it in assembler, > > or (2) Use gotos? I'd rather find a better algorithm than pick up a nanosecond here and there hacking in goto's. Roger Reid