Xref: utzoo misc.misc:5922 comp.misc:5938 Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!hc!lanl!jlg From: jlg@lanl.gov (Jim Giles) Newsgroups: misc.misc,comp.misc Subject: Re: The "evil" GOTO (Was: 25 Years of BASIC) Message-ID: <13113@lanl.gov> Date: 4 May 89 22:38:12 GMT References: <1814@ubu.warwick.UUCP> Organization: Los Alamos National Laboratory Lines: 74 From article <1814@ubu.warwick.UUCP>, by mirk@warwick.UUCP (Mike Taylor): > printf ("Enter your sex: "); > LABEL: gets (sex); > if (sex != "m" && sex != "f") { > printf (" or only: "); > GOTO LABEL; > } > > Better examples abound, but in time honoured way, elude me now that I > need them. How many times have you seen people using "for (;;;) ... > break;" when you *know* that by "break" they mean "GOTO"? Actually, in the above example, "break" is a better way. This is the classic "loop and a half" problem for which the "break" statement exists. So the following is a better suggestion: printf ("Enter your sex: "); for (;;) { gets (sex); if (sex == "m" || sex == "f") break; printf (" or only: "); } Note: since C doesn't have multi-level breaks, GOTO must be used to escape from nested constructs. This does not mean that I oppose GOTOs in a programming language. In fact, there _are_ situations in which GOTO is the most readible and efficient means of flow control. Consider the situation in which two branches of a conditional merge again: if (cond1) { [...A...] /* lots of code */ goto LABEL;} else if (cond2) { LABEL: [...B...] /* lots more code */ } After executing sequence 'A', the rest of the action for 'cond1' is identical to the action for 'cond2'. The suggested "structured" fixes to this sequence include: 1) put sequence 'B' in a separate procedure and call it from both places 2) duplicate sequence 'b' in both places 3) set a flag variable to hold the condition that 'B' needs to be executed, and put 'B' after the original 'if' sequence enclosed in another conditional. The problem with 1) is that it introduces a speed penalties due to the procedure call - not efficient. Solution 2) imposes a code space penalty as well as making code maintenance difficult (you must always remember to update _both_ versions of 'B'). Solution 3) imposes a speed penalty for setting and testing the flag, a space penalty for the extra variable and the code to manipulate it, and it is _less_ readible than the version already given above! This conditional-merge problem is one of the only two places where I still use GOTO in my programs - the other case is the classic error-exit from deep within a nested structure (exception handling, if available, would satisfy this requirement). Unfortunately, the above construct is illegal in many "structured" programming languages (including - of all things - Fortran). They seem to feel that jumping into a compound statement from the outside is bad practice. Yet, I run into this case all the time (even on very short programs (<1000 lines) I usually run into this at least once). And there is no "structured" way around it! Note: I always make a distinction between well structured programs and well "structured" programs. The later are those which conform to the anti-GOTO religion without regard to the actual quality of the code itself. A well "structured" program is often a very badly structured one.