Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!seismo!think!rst From: rst@think.COM (Robert Thau) Newsgroups: comp.lang.c Subject: Re: goto's in C: an opinion... Message-ID: <6603@think.UUCP> Date: Sat, 18-Jul-87 13:57:14 EDT Article-I.D.: think.6603 Posted: Sat Jul 18 13:57:14 1987 Date-Received: Sun, 19-Jul-87 01:00:35 EDT References: <3289@bigburd.PRC.Unisys.COM> <7571@beta.UUCP> Sender: news@think.UUCP Reply-To: rst@godot.think.com.UUCP (Robert Thau) Organization: Thinking Machines Corporation, Cambridge, MA Lines: 121 Keywords: C, goto, style 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 ...