Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!seismo!gatech!bloom-beacon!wesommer From: wesommer@athena.mit.edu (William Sommerfeld) Newsgroups: comp.lang.c Subject: Re: goto's in C: an opinion... Message-ID: <1164@bloom-beacon.MIT.EDU> Date: Fri, 17-Jul-87 23:32:49 EDT Article-I.D.: bloom-be.1164 Posted: Fri Jul 17 23:32:49 1987 Date-Received: Sat, 18-Jul-87 18:28:27 EDT References: <3289@bigburd.PRC.Unisys.COM> <7571@beta.UUCP> Sender: daemon@bloom-beacon.MIT.EDU Reply-To: wesommer@priam.UUCP (William Sommerfeld) Organization: Massachusetts Institute of Technology Lines: 78 Keywords: C, goto, style 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