bill@twwells.com (T. William Wells) (09/14/89)
In article <7917@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes: : ) Well yeah, so would I. But what is the bug? I've just reread the code : ) and don't see where I've broken it. I'm prepared to be red-faced : ) because this has go to be something really obvious, but what? : : DON'T PANIC. Good thing, my Panic Button is broken! I hit it too many times already today. :-) : Your face is going to be just fine. But it's lucky for me that I don't blush. : You didn't misread it. I did. In fact, I read it half a dozen times before I saw : the "break" at the end of the while-loop. : : Boy, that's wierd. : : No offense, but you won't catch me using this device. Different strokes, : I guess. Yeah. No doubt the strangeness of it will put many people off. Well one day I just got so tired of icky argument parsing loops that I devised a thing with a break past the bottom of the switch. Once I realized that this solved many cases of the two level break problem, I started using it fairly regularly. For me it is now a familiar idiom; I don't have any problem with it any more. My feeling is that C idiom is already sufficiently strange, what's one more wierdness among programmers? :-) As far as I'm concerned, *every* method of solving the two level break problem is flawed in one way or another. The right solution is a language innovation, but I'm not going to hold my breath. But anyway, that isn't really why I'm posting. You may recall an article I wrote on this subject the last time it came 'round (over in comp.misc). I'm enclosing an updated version here because it really *is* the final word on goto. (Ok, it certainly could be elaborated. But as the guy said, all else is commentary....) --- Under ordinary circumstances, there is exactly one place I know of where a human C coder might properly use a goto. This is to implement multi-level breaks and continues. I say this, having written hundreds of thousands of C code, and managed several programmers also writing mainly C code. I have programmed in C since 1981 and have NEVER used a goto. We have uncounted megabytes of C code written in-house. There are no gotos in it. With that up front, let me say why the goto discussion is really fruitless: it completely misses the point. People have observed that gotos are used in a lot of bad code. From this it is concluded that gotos are bad. This is really bad logic. Try this: programmers have been observed to write bad code; therefore, programmers are bad! THERE IS NOTHING INHERENTLY WRONG WITH GOTO. And how do I reconcile with my comments above? Wait and see... One important point in the structured programming argument is this: every program has a control structure; some of these control structures are better than others. Whether you use gotos or some other language feature to implement the control structure does not change what the control structure is, nor does it affect (much) the goodness of the control structure. The quality of your program is strongly influenced by the quality of its control structures. Furthermore, you want that control structure to be obvious and understandable to the reader of the program. This implies that you use language features that make your use of a control structure as obvious as possible. So, the first question should be: what are the good control structures? The second question is then: given a particular language, which control structures should be implemented? And how? Ok, so what makes a control structure good? Well, the basic answers are: a control structure is good if it is 1) appropriate to solving programming problems. 2) easy to write. 3) easy to understand. 4) easy to maintain. 5) ... add your own as long as they do not contradict the above There are obviously lots of control structures that meet these requirements and you do not have to use all of them. In fact, you should pick a set of those which are most appropriate for your programming environment and use just those. This set should be, in some sense, a minimum one; for example, if you have two control structures which can accomplish the same thing, but one is easier to use than the other (for you), pick the easier one and forget the other. If you code a repeat forever loop as while (1), always do it that way, don't switch to for(;;) every third tuesday. All other things being equal, a smaller number of control structures helps make your program easier to understand. Now, I hope my claim about our C programs is understandable. But if not, here is what it amounts to: we have chosen a set of control structures which is appropriate to programming in C, for the kind of programming tasks that we do. It happens that, while this set of control structures includes multi-level breaks and continues (which could be implemented with a goto), we have never had need to implement one with a goto. Given the amount of code involved, it seems to me that one might never have a good reason to use an explicit goto in C code. For other languages, one would come to a different conclusion. In assembly language (unless you have a fairly powerful assembler), *every* control structure is implemented with a goto. In COBOL, many control structures are implemented with a goto. In Pascal, there are just a few things that are best expressed with a goto. In most varieties of Lisp, there is, from what I've seen, always a better way than a goto. There is, however, a reason to avoid naked gotos in your code: for all other keywords, the control structure being implemented is obvious. But for goto it isn't. One way to make the control structure obvious is to properly name the target of the loop (e.g., end_of_main_traversal_loop). Another is to make the control structure obvious by clothing the goto in some preprocessor magic. As an example, suppose that you are using the state machine control structure. One way to code it is: state = STATE_INIT; while (state != STATE_DONE) { switch (state) { case STATE_INIT: ... } } However, this is not the most efficient way to do it. If efficiency were a concern (and you knew that you weren't going to deal with a brain-damaged optimizer that won't optimize functions that have even a single goto) you could also implement it as: /* Wherever you see these macros being used, you will be seeing a state machine. The state macro defines the start of a state. The enter_state macro causes a transfer of control to another state of the same machine. The leave macro sends you to the end of the machine, the end_machine macro marks the place where the machine ends. */ #ifndef SDEBUG #define state(m, label) m##label:; #define end_machine(m) m##_finish:; #else #define state(m, label) printf("warning: falling though\n"); \ m##label: printf("entering state " #label \ " of machine " #m "\n"); #define end_machine(m) m##_finish:printf("leaving machine " #m "\n"); #endif #define enter_state(m, label) goto m##label #define leave(m) goto m##_finish state(input_interp, INIT) ... code for the INIT state enter_state(input_interp, MORE); state(input_interp, MORE) ... code for the MORE state if (nomore) { leave(input_interp); } else if (error) { enter_state(input_interp, RECOVER); } enter_state(input_interp, MORE); state(input_interp, RECOVER) ... code for the RECOVER state enter_state(input_interp, FUDGE_INPUT); ... end_machine(input_interp) Some of you will no doubt be thinking: but why should I go to all this effort when I could just use the goto directly? Well, if you do only one kind of thing with goto, I don't really see any reason why not (but I do think your program should include a comment saying what control structure you have implemented using the goto). If, however, you have more than one way of using goto, you should clothe the gotos somehow so that the reader of the program knows what control structures each of your gotos belongs to. --- Bill { uunet | novavax | ankh | sunvice } !twwells!bill bill@twwells.com
mcdonald@uxe.cso.uiuc.edu (09/15/89)
Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Goto Considered Harmful I would consider a goto harmful IF and ONLY IF it produced a bug in a program. Doug McDonald
gwyn@smoke.BRL.MIL (Doug Gwyn) (09/17/89)
In article <225800222@uxe.cso.uiuc.edu> mcdonald@uxe.cso.uiuc.edu writes: >I would consider a goto harmful IF and ONLY IF it produced a bug in a program. The argument against use of GOTO is that it does lead to unreliable code. I think this is indisputable for uncontrolled use of GOTO. Some tightly-disciplined uses seem to me to not get in the way of verifying code correctness.
jeenglis@nunki.usc.edu (Joe English) (09/17/89)
mcdonald@uxe.cso.uiuc.edu writes: >I would consider a goto harmful IF and ONLY IF it produced a bug in a program. How about a bunch of goto's that rendered the code totally unreadable? Have you seen the recent posting of the permutation algorithm translated from Fortran? That's what's considered harmful about it. --Joe English jeenglis@nunki.usc.edu
ijk@cbnewsh.ATT.COM (ihor.j.kinal) (09/26/89)
In article <225800222@uxe.cso.uiuc.edu>, mcdonald@uxe.cso.uiuc.edu writes: > > I would consider a goto harmful IF and ONLY IF it produced a > bug in a program. > Well, what if it took the NEXT poor programmer who maintains that program an extra week to understand what it does ??? Or even an extra minute ??? [each and every time it was examined - it might add up to a lot]. Suppose that the goto was bug-free, but increased the chance of bugs in subsequent fixes/ changes to the program?? Even if the bugs never actually occur? I think we forget that in the software world, MOST of the effort goes in maintaining code that was written by someone else. Ihor Kinal <include standard disclaimers.
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (09/26/89)
In article <4208@cbnewsh.ATT.COM>, ijk@cbnewsh.ATT.COM (ihor.j.kinal) writes: | Well, what if it took the NEXT poor programmer who maintains that | program an extra week to understand what it does ??? Or even | an extra minute ??? [each and every time it was examined - it might | add up to a lot]. It depends on what you use instead of the goto. If you compare a goto against setting a flag variable and testing it to break out of three levels of loop, the goto is certainly easier to understand, particularly if the loops have a lot of code. If you use a goto where a break would do it makes the code more subject to error (you can now mess up placement of the goto or the label). -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon
kim@kim.misemi (Kim Letkeman) (09/27/89)
In article <514@crdos1.crd.ge.COM> davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes: >In article <4208@cbnewsh.ATT.COM>, ijk@cbnewsh.ATT.COM (ihor.j.kinal) writes: >| Well, what if it took the NEXT poor programmer who maintains that >| program an extra week to understand what it does ??? Or even >| an extra minute ??? [each and every time it was examined - it might >| add up to a lot]. > It depends on what you use instead of the goto. If you compare a goto >against setting a flag variable and testing it to break out of three >levels of loop, the goto is certainly easier to understand, particularly ^^^^^^^^^^^^ >if the loops have a lot of code. If you use a goto where a break would ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >do it makes the code more subject to error (you can now mess up >placement of the goto or the label). If you have three levels of looping with "a lot of code" in them, then a goto versus a flag is hardly your biggest problem. If you keep your programs well structured, with *small* functions that fan out smoothly from control level functions to low level (working) functions, then this situation just doesn't arise. Only when you succumb to the temptation to add "just a little more" functionality to an existing function without properly restructuring it into its component functional units will you end up with rat's nests that require gotos to handle control flow. I apologize if this message has a pious tone. It is not intended to be a preaching article, but this thread has been filled with attacks on and defenses of gotos without really acknowledging the fact that a goto is usually the result of too much complexity in the control flow. The complexity arises (usually) when there isn't the time or inclination to properly structure the code in question. I have read and applied the concepts discussed within the book Software Tools (to name perhaps the most famous of this ilk) and it works. Code need not be ugly. It need not contain gotos (except in the most extreme of circumstances.) And optimization of that code by the compiler is a red herring if ever there was one. Code that is badly structured enough to require a goto already has macro algorithmic problems that can't be solved by a micro optimizer. (And as most know, macro algorithm problems account for orders of magnitude more execution time than the more micro compiler optimization problems.) -- Kim Letkeman uunet!mitel!spock!kim
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (09/28/89)
In article <1017@kim.misemi>, kim@kim.misemi (Kim Letkeman) writes: | If you keep your programs well structured, with *small* functions that | fan out smoothly from control level functions to low level (working) | functions, then this situation just doesn't arise. Size and structure are not exclusive. A switch within two for loops seems like a reasonable structure to me, but it can contain a lot of cases. If the code for each case is small, say 1-2 statements each, using a table lookup and procedure per case can add a lot of overhead, and encourages pollution of global namespace to make variables available. All big procedures are not badly structured, and efforts to keep the lines per procedure down to some arbitrary limit cost time to write, to run, and to maintain. | I apologize if this message has a pious tone. It certainly could be read that way... -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon
kim@kim.misemi (Kim Letkeman) (09/29/89)
In article <598@crdos1.crd.ge.COM> davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes: > In article <1017@kim.misemi>, kim@kim.misemi (Kim Letkeman) writes: > | If you keep your programs well structured, with *small* functions that > | fan out smoothly from control level functions to low level (working) > | functions, then this situation just doesn't arise. > Size and structure are not exclusive. A switch within two for loops > seems like a reasonable structure to me, but it can contain a lot of > cases. If the code for each case is small, say 1-2 statements each, > using a table lookup and procedure per case can add a lot of overhead, > and encourages pollution of global namespace to make variables > available. This is a fairly rare occurence in my experience. In a situation where you have a couple of nested loops where the lowest level contains many cases with only one or two lines each, a clean way in which to express this is to separate the function being performed at the lowest level into a separate procedure. (You can pass that procedure the data it needs.) This leaves the higher level "control" procedure at a reasonable size with its structure quite obvious. Essentially, this part of the goto thread is about (IMO) software structure as it pertains to readablility and maintainability. The optimization part of this thread is a separate issue. So, if we consider readability and maintainability as desirable attributes (likely a general consensus), then anything that contributes to that is also desirable. Small procedures contribute. > All big procedures are not badly structured, and efforts to keep the > lines per procedure down to some arbitrary limit cost time to write, to > run, and to maintain. True. A big procedure is not necessarily badly structured. But that does not make it easy to read. In fact, a procedure that grows beyond your immediate field of view (24 lines on terminals, more on listings and workstations) is *automatically* harder to read since you have less context within your immediate grasp. Some procedures *must* grow larger than your normal field of view. Granted. But this does not excuse someone who habitually codes 100 line functions or procedures. The circumstances in which a single control procedure needs to be longer than say, 50 lines (which I consider too large as it is) are quite rare. If you examine almost any procedure of this size, you will see that it performs functions at two levels of control. Or even worse, two functions at the same level of control. Or really horrible, two completely independent functions at the same level of control where a switch passed in determines the action performed. As a general principle, writing a single procedure to perform a single function (task) is the whole point behind so called structured programming. Coupling and cohesion and all that. So where does the goto fit after all this? It doesn't. > | I apologize if this message has a pious tone. > It certainly could be read that way... True. But then so could anything written by someone with strongly held beliefs. Especially if you disagree with that person's most basic premises. -- Kim Letkeman uunet!mitel!spock!kim
jeenglis@nunki.usc.edu (Joe English) (09/30/89)
[ Sorry if this is a repost; I forgot to expand the tabs and cancelled the original as quick as I could... ] kim@kim.misemi (Kim Letkeman) writes: >If you examine almost any procedure of [large] size, you will see that it >performs functions at two levels of control. Or even worse, two >functions at the same level of control. Or really horrible, two >completely independent functions at the same level of control where a >switch passed in determines the action performed. > >As a general principle, writing a single procedure to perform a single >function (task) is the whole point behind so called structured >programming. Coupling and cohesion and all that. > >So where does the goto fit after all this? It doesn't. What kind of control flow you use doesn't have that much to do with coupling or cohesion; the goto still has some valid uses. For example: void foo() { int k; initialization_sequence; while (k = get_input()) { switch (k) { case 'A' : a_action(); break; case 'B' : b_action(); break; ... case 'Q' : q_termination_stuff(); goto done; case 'X' : x_termination_stuff(); goto done; } end_loop_processing; } done: cleanup_sequence; return; } This, I submit, is an example of a cohesive function that uses a goto. If end_loop_processing and cleanup_sequence are nontrivial, this can't be cleanly done any other way. This example wasn't just invented to prove a point; I actually wrote something similar once. (I shouldn't have said that... now half the net will probably accuse me of dubious programming practices :-) Another example of (I think) a valid use for the goto is the multi-level exception handling case posted a while back. --Joe English jeenglis@nunki.usc.edu
kim@kim.misemi (Kim Letkeman) (10/02/89)
In article <5416@merlin.usc.edu> jeenglis@nunki.usc.edu (Joe English) writes: >kim@kim.misemi (Kim Letkeman) writes: >>[blah blah blah] >>As a general principle, writing a single procedure to perform a single >>function (task) is the whole point behind so called structured >>programming. Coupling and cohesion and all that. >> >>So where does the goto fit after all this? It doesn't. >What kind of control flow you use doesn't have >that much to do with coupling or cohesion; the >goto still has some valid uses. For example: You got me. I took a chance and mixed two points in successive paragraphs without making my point particularly clear. I was really trying to say that I feel that structured programming concepts are adequate for the vast majority of situations and that gotos are only necessary in extreme cases. I apologize for the lack of clarity there. >void foo() { > int k; > initialization_sequence; > > while (k = get_input()) { > switch (k) { > case 'A' : a_action(); break; > case 'B' : b_action(); break; > ... > case 'Q' : q_termination_stuff(); goto done; > case 'X' : x_termination_stuff(); goto done; > } > end_loop_processing; > } >done: > cleanup_sequence; > > return; >} >This, I submit, is an example of a cohesive function >that uses a goto. If end_loop_processing and >cleanup_sequence are nontrivial, this can't be cleanly >done any other way. I agree that this is an example of a cohesive function if the end_loop_processing and cleanup_sequence is fairly trivial. If not, it is likely that they are performing fairly significant functions that are not entirely related to the actual running of the pseudo state machine. In that case, they would be better isolated in their own procedures. This improves cohesion and makes this procedure more readable. (Your example is quite readable as shown, but would deteriorate with additional code.) >This example wasn't just invented to prove a point; I >actually wrote something similar once. (I shouldn't >have said that... now half the net will probably >accuse me of dubious programming practices :-) Just one (very quickly conceived) alternative: void foo() { int k; int done; initialization_sequence (some parms?); /* function if non-trivial */ done = 0; while (k = get_input()) { switch (k) { case 'A' : a_action(); break; case 'B' : b_action(); break; ... case 'Q' : q_termination_stuff(); done = 1; case 'X' : x_termination_stuff(); done = 1; } if (done) break; end_loop_processing (some parms?); /* function if non-trivial */ } cleanup_sequence (some parms?); /* function if non-trivial */ return; } This version looks as good to me and if the case statement gets long it means you don't get your "oh no ... a goto" bell rung every time you encounter a termination situation. To make it read better, I might use a state variable with readable names in an enumeration, or at least constants like TRUE and FALSE. But the point is that this case is not (IMO) extreme enough to warrant a goto. >Another example of (I think) a valid use for the >goto is the multi-level exception handling case >posted a while back. I believe someone posted a version without a goto. It looked fine to me. >--Joe English -- Kim Letkeman uunet!mitel!spock!kim
jlg@lanl.gov (Jim Giles) (10/03/89)
From article <1044@kim.misemi>, by kim@kim.misemi (Kim Letkeman): > True. A big procedure is not necessarily badly structured. But that > does not make it easy to read. In fact, a procedure that grows beyond > your immediate field of view (24 lines on terminals, more on listings > and workstations) is *automatically* harder to read since you have > less context within your immediate grasp. Splitting some codes into several different routines may also be *automatically* harder to read. There is a difference between modularization and fragmentation. The line between varies according to the type of problem and the style of programming used. I have seen thousand line codes which were perfectly coherent. I have also seen 24 line code which should have been modularized.
kim@kim.misemi (Kim Letkeman) (10/03/89)
In article <14061@lanl.gov> jlg@lanl.gov (Jim Giles) writes: >Path: mitel!uunet!cs.utexas.edu!tut.cis.ohio-state.edu!rutgers!cmcl2!lanl!jlg >From: jlg@lanl.gov (Jim Giles) >Newsgroups: comp.lang.c >Date: 2 Oct 89 17:36:57 GMT >References: <1044@kim.misemi> >Organization: Los Alamos National Laboratory >Lines: 13 >From article <1044@kim.misemi>, by kim@kim.misemi (Kim Letkeman): >> True. A big procedure is not necessarily badly structured. But that >> does not make it easy to read. In fact, a procedure that grows beyond >> your immediate field of view (24 lines on terminals, more on listings >> and workstations) is *automatically* harder to read since you have >> less context within your immediate grasp. >Splitting some codes into several different routines may also be >*automatically* harder to read. There is a difference between >modularization and fragmentation. The line between varies according >to the type of problem and the style of programming used. I have >seen thousand line codes which were perfectly coherent. I have also >seen 24 line code which should have been modularized. I think that I made it perfectly clear later on in that article that I advocate structured coding that follows the rules of functional cohesion. This precludes the very stupid practice of cutting up cohesive functions just for the sake of modularization. I find it hard to believe that you have seen a function that was 1,000 lines long and was still "perfectly coherent" (I assume that you are using this as a euphomism for readable.) I do believe that you have seen 24 line functions that could have been modularized. Lots of people just write that way. This thread is getting a bit twisted by those who feel that one pathological case can prove that gotos are ok or that long procedures are unavoidable. I'd even start to believe this myself if it weren't for the fact that most of the cases put forth were refuted by others with code that appeared to do the job just as well without unnecessarily breaking the rules of structured coding. -- Kim Letkeman uunet!mitel!spock!kim
peter@ficc.uu.net (Peter da Silva) (10/03/89)
The last time I looked at a stdio library there was a goto in _doprnt:
if you see a %
handle number.number stuff
see what the format is
switch(format) {
case 'd':
base = 10;
if(number < 0) {
sign = '-';
number = -number;
} else
sign = 0;
...
goto donum;
case 'u':
base = 10;
sign = 0;
...
goto donum;
case 'o':
base = 8;
sign = 0;
...
goto donum;
case 'x':
base = 16;
sign = 0;
...
goto donum;
donum:
format number with base, sign, etc.
(yes, I know this won't work for -32768. It's just an example)
You could probably rearrange this to not need the gotos, but I really
don't see that it would make the code any clearer. That's the problem.
Sometimes just leaving the goto in makes the code easier to read.
Really. A goto isn't the devil, it's just a seldom used ingredient.
Like blue food coloring... you don't use it often, but would you ban
it from the kitchen?
--
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
"That is not the Usenet tradition, but it's a solidly-entrenched U
delusion now." -- brian@ucsd.Edu (Brian Kantor)
bkottman@thor.wright.edu (Brett Kottmann) (10/05/89)
From article <6396@ficc.uu.net>, by peter@ficc.uu.net (Peter da Silva): / The last time I looked at a stdio library there was a goto in _doprnt: / / if you see a % / handle number.number stuff / see what the format is / / switch(format) { / case 'd': / base = 10; / if(number < 0) { / sign = '-'; / number = -number; / } else / sign = 0; / ... / goto donum; / case 'u': / base = 10; / sign = 0; / ... / goto donum; / case 'o': / base = 8; / sign = 0; / ... / goto donum; / case 'x': / base = 16; / sign = 0; / ... / goto donum; / donum: / format number with base, sign, etc. / This must be really old code since in C, execution falls through to the next case anywasy; in every case without the goto, it would hit donum: anyways...(albiet after trying the rest of the case statements) A break usually replaces the goto in that type of code.
cpcahil@virtech.UUCP (Conor P. Cahill) (10/05/89)
In article <725@thor.wright.EDU>, bkottman@thor.wright.edu (Brett Kottmann) writes: > From article <6396@ficc.uu.net>, by peter@ficc.uu.net (Peter da Silva): > / The last time I looked at a stdio library there was a goto in _doprnt: > / > / [sample switch with gotos deleted] > / > This must be really old code since in C, execution falls through > to the next case anywasy; in every case without the goto, it would hit > donum: anyways...(albiet after trying the rest of the case statements) > A break usually replaces the goto in that type of code. The operations of a switch statement make a single comparison and jump to the specified case (or default). Once that jump has been made, there is no further "trying the rest of the case statements". without the listed gotos, the code would always act like the last case (base=16...). Since the entir code sample was not presented, one cannot guess as to the likelyhood that the goto's could easily be replaced with a break. -- +-----------------------------------------------------------------------+ | Conor P. Cahill uunet!virtech!cpcahil 703-430-9247 ! | Virtual Technologies Inc., P. O. Box 876, Sterling, VA 22170 | +-----------------------------------------------------------------------+
gwyn@smoke.BRL.MIL (Doug Gwyn) (10/05/89)
In article <725@thor.wright.EDU> bkottman@thor.wright.edu writes: > This must be really old code since in C, execution falls through >to the next case anywasy; in every case without the goto, it would hit >donum: anyways...(albiet after trying the rest of the case statements) > A break usually replaces the goto in that type of code. For the benefit of anyone who might have been misled by that response, I should point out that bkottman completely misread the code. Those particular gotos could not have been omitted unless the code were substantially rearranged, and the age of the code has nothing to do with it.
peter@ficc.uu.net (Peter da Silva) (10/05/89)
Code deleted, see parent article. In article <725@thor.wright.EDU>, bkottman@thor.wright.edu (Brett Kottmann) writes: > This must be really old code since in C, execution falls through > to the next case anywasy; in every case without the goto, it would hit > donum: anyways...(albiet after trying the rest of the case statements) Yes, but if you looked at what the code *does*, you would see that you don't want to execute those statements. > A break usually replaces the goto in that type of code. In this case it wouldn't. Here's a goto-less version of the multiple lead-in case that should have you all puking: switch(format_char) { case 'd': the stuff I put under d; if(0) { case 'o': the stuff I put under o; if(0) { case 'x': the stuff I put under x; if(0) { case 'u': the stuff I put under u; }}} the stuff I put under donum; } I know it's not the best way to remove the gotos, but it's legal C without a single "unstructured" element. It's always possible to "remove" the gotos. It's not always worth it. Structured programming is a discipline, not a game. -- Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation. Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-' ``I feel that any [environment] with users in it is "adverse".'' 'U` -- Eric Peterson <lcc.eric@seas.ucla.edu>
bph@buengc.BU.EDU (Blair P. Houghton) (10/06/89)
In article <6430@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes: >Code deleted, see parent article. >Here's a goto-less version of the multiple >lead-in case that should have you all puking: Hwuawp! Thanks, Pete. Had the wrong thing for lunch, anyway... :-) > switch(format_char) { > case 'd': the stuff I put under d; if(0) { > case 'o': the stuff I put under o; if(0) { > case 'x': the stuff I put under x; if(0) { > case 'u': the stuff I put under u; }}} > the stuff I put under donum; > } > >I know it's not the best way to remove the gotos, but it's legal C >without a single "unstructured" element. I'd go out of my way to fix it so that I could just do switch(format_char) { case 'd': the stuff I put under d; break; case 'o': the stuff I put under o; break; case 'x': the stuff I put under x; break; case 'u': the stuff I put under u; break; } the stuff I put under donum; Even if it meant (Gasp!) declaring a few variables outside the switch and structuring the rest of my program in such a way as to prevent needing such kludgery... --Blair "At least they'd get initialized properly..."
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/06/89)
In article <4447@buengc.BU.EDU>, bph@buengc.BU.EDU (Blair P. Houghton) writes: | I'd go out of my way to fix it so that I could just do | | switch(format_char) { | case 'd': the stuff I put under d; break; | case 'o': the stuff I put under o; break; | case 'x': the stuff I put under x; break; | case 'u': the stuff I put under u; break; | } | | the stuff I put under donum; This doesn't do the same thing at all. In the original the "donum:" code was only executed if a case was matched in the switch. Your example executes it everytime. As you mentioned but didn't show you would have to define a bunch of flags and stuff to make this work. -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon
bph@buengc.BU.EDU (Blair P. Houghton) (10/07/89)
In article <867@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes: >In article <4447@buengc.BU.EDU>, bph@buengc.BU.EDU (Blair P. Houghton) writes: > >| I'd go out of my way to fix it so that I could just do >| >| switch(format_char) { >| case 'd': the stuff I put under d; break; >| case 'o': the stuff I put under o; break; >| case 'x': the stuff I put under x; break; >| case 'u': the stuff I put under u; break; >| } >| >| the stuff I put under donum; > > This doesn't do the same thing at all. In the original the "donum:" >code was only executed if a case was matched in the switch. Your example Quite so. >executes it everytime. As you mentioned but didn't show you would have >to define a bunch of flags and stuff to make this work. Not really: switch(ch = format_char) { /* Switch body exactly as above */ } switch(ch) { case 'd': case 'o': case 'x': case 'u': the stuff I put under donum; break; default: break; } I only did the extra assignment to give the switch body full use of the variable format_char. It may well be unnecessary in our hypothetical example. --Blair "One variable of indeterminate necessity and an improvement in the maintainability of the do-for-all-flags code. How many marks out of ten, Dr. Structmember-Offset?"
dwho@nmtvax.nmt.edu (David Olix) (10/07/89)
In article <867@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes: > > [...] In the original the "donum:" >code was only executed if a case was matched in the switch. [...] Would the following be too terribly unreadble? case_matched = 1; switch (format_char) { case 'd': the stuff I put under d; break; case 'o': the stuff I put under o; break; case 'x': the stuff I put under x; break; case 'u': the stuff I put under u; break; default: case_matched = 0; } if (case_matched) the stuff I put under donum; I'm not really arguing whether or not to use goto's here; I'm arguing that adding one flag (case_matched) does not a 'whole lot of flags' make. --David Olix (dwho@nmtvax.nmt.edu)
bph@buengc.BU.EDU (Blair P. Houghton) (10/07/89)
Rich Salz pointed out (via email) that I'd made a mistake in my rearrangement of the switch/goto construct currently under debate. I've come up with an idea I'd like to see rent asunder by the people I know will do it best. :-) (BTW, I happen to think there's absolutely nothing wrong with goto's, so long as you think in terms of structure and use the goto's only when they appear perfectly apt.) A correct functionality, as Rich showed it, without gotos: is_doxu = 0; switch(format_char) { case 'c': stuff; break; case 's': stuff; break; case 'd': the stuff I put under d; is_doxu++; break; case 'o': the stuff I put under o; is_doxu++; break; case 'x': the stuff I put under x; is_doxu++; break; case 'u': the stuff I put under u; is_doxu++; break; } if (is_doxu) { stuff } This is probably version requiring least effort to maintain. In order to add or delete cases in the switch, you only have to copy any other case and change the case-constant to fit the new case. The way I showed, you have to do it the same in both switches, so it's nominally more difficult. The way I'd probably do it if I had tried it from scratch is to in-my-head expand the "is_doxu" logic and do switch(format_char) { case 'c': stuff under c; break; case 's': stuff under s; break; case 'd': stuff under d; break; case 'o': stuff under o; break; case 'x': stuff under x; break; case 'u': stuff under u; break; } if ( c == 'd' || c == 'o' || c == 'x' || c == 'u' ) { stuff for doxu cases } Switch provides a method of conditionally splitting the flow-graph, but there doesn't exist a dual of it that would describe the joining of some subset of the split branches of the flow-graph. Something like switch(fc) { case 'c': stuff under c; break; case 's': stuff under s; break; ... case 'u': stuff under u; break; cases 'd', 'o', 'x', 'u': stuff for doxu cases; break; cases 'd', 'c', 'u': stuff for dcu cases; break; } and let the compiler figure out how to keep track of the flow (which obviously can get very very complicated very quickly) of the machine language, which only knows from JUMP, JNZ, etc., anyway... Notice that with gotos you could use maybe two or three common-cases, and then only if you added a lot of extra control structures to track the flow. By adding the "cases" keyword, you have the compiler do it in whatever way its graph-reduction and conflict-arbitration routines decide is the best. I haven't really considered order-of-evaluation. This is a high-level, low-baked concept at the moment. The obvious method is to just replace every "cases 'x','y'" with the equivalent of "if ( (fc=='x') || (fc=='y') )". This gets rid of the common implementation that case-labels will be treated as goto-targets, but that's an assumption, anyway. Nothing stops a compiler from just using a skein of "if...then...else-if...then...else-if...then...else" constructs as a translation for the switch. The alternative is to turn common code into a subroutine, and insert its invocation immediately before the machine-code construct that implements the break keyword in each branch. Given that there are a number of programming tools being researched that use a _graphical_ interface to code a program (i.e., you just type a few logical statements into a box that looks like a flowchart symbol and the computer writes the actual C code for you; create a few arrows connecting boxes and you have flow-of-control) something like "cases" would be very handy. Then again, a the computer doing the coding wouldn't have any compunction in using gotos all over the place. What we're doing here (in this discussion) is optimizing structurally, for the benefit of programmers, while the compiler is obligated to optimize (most often) for speed first and size second, and readability of the flow structure is moot. What we're also doing here (and now) is implementing this desired flow construct using the current definition of the language. There's nothing impossible or ambiguous about using the "if (0) {...}" pseudo-break to get the job done. We'll all still get paid for doing it. --Blair "'Stop the Standard! We have a new keyword to add!' ... :-) You can come down off the ceiling, now, Doug..." P.S. Exercise: Show that allowing multiple "case 'x':" labels for each constant 'x' would give exactly the results that the "cases" keyword provides...the meaning of "break" changes, however, from "termination of the smallest enclosing while, do, for, or switch," to "termination of the smallest enclosing while, do, or for; or, jump to the next matching case-label in the smallest enclosing switch; or, iff no further cases match, terminate the smallest enclosing switch..."
peter@ficc.uu.net (Peter da Silva) (10/07/89)
In article <4462@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes: > switch(ch = format_char) { > /* Switch body exactly as above */ > } > switch(ch) { /* duplicate switch with just the donum code */ > } I'm not at all sure this is clearer than the goto-full version. You have taken a single logical operation and split it in two parts. If you consider the goto the work of the devil, then of course this represents progress. If you don't, but consider it a rather esoteric tool to be used for unusual circumstances, then it doesn't. Consider that it's now less maintainable, since you have the same set of cases included in two places. What happens if you put in a '%b' format? Now you have two places to add the code for printing in binary. Personally, I find gotos not a lot diffreent from switches and return statements, and a lot less confusing than longjmp. -- Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation. Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-' "Seems to me that posting an entire RFC in PostScript is like posting a 'U` Sun-3 binary to comp.sources.unix." -- sgrimm@sun.com (Steven Grimm)
peter@ficc.uu.net (Peter da Silva) (10/08/89)
In article <154@nmtvax.nmt.edu> dwho@nmtvax.UUCP (David Olix) writes: >Would the following be too terribly unreadble? [does "donum" code if the case didn't match] In the original situation where you would have had a bunch of flags for format string, format float, and so on? Yes. -- Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation. Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-' "Seems to me that posting an entire RFC in PostScript is like posting a 'U` Sun-3 binary to comp.sources.unix." -- sgrimm@sun.com (Steven Grimm)
ggg@sunquest.UUCP (Guy Greenwald) (10/09/89)
The structure switch(c) { case 'd': ... goto something; case 'u': ... goto something; ...: ... goto something; something: /* Where's the default? */ } can easily be replaced with switch(c) { case 'd': ... something(); break; case 'u': ... something(); break; ...: ... something(); break; default: ... break; } with no loss of clarity. Other transformations are possible, too. The fact that some standard library implementation uses it isn't convincing, either. I had to write my own strpbrk for VAX C because the standard library routine performed miserably for the long strings my code was processing (10K+). Useful routines can be poorly implemented. The first implementation given above will execute slightly faster than the second, but why is it "easier to read?" I can't see that the goto's are really necessary. (something() could be implemented as a macro if it needs to be faster.)
connelly@deimos.cis.ksu.edu (Paul Connelly) (10/10/89)
In article <4467@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
-P.S. Exercise: Show that allowing multiple "case 'x':" labels
-for each constant 'x' would give exactly the results that the "cases"
-keyword provides...the meaning of "break" changes, however, from
-"termination of the smallest enclosing while, do, for, or switch," to
-"termination of the smallest enclosing while, do, or for; or, jump
-to the next matching case-label in the smallest enclosing switch;
-or, iff no further cases match, terminate the smallest enclosing switch..."
I think "break" should take you out of whatever, less confusion that way,
but, since we're living in a dream world, let's use "continue" to jump
to the next matching case constant in the smallest enclosing switch. :-)
--
W. Paul Connelly Dept. of Comp. & Info. Sci.
BITNET: connelly@ksuvax1.bitnet Kansas State University
INTERNET: connelly@ksuvax1.cis.ksu.edu Manhattan, KS 66506
UUCP: ...!{rutgers,texbell}!ksuvax1!connelly (913) 532-6350
peter@ficc.uu.net (Peter da Silva) (10/10/89)
The referenced article discusses replacing the Goto with a subroutine call. The question is raised... why would that reduce clarity? In a block-structured language it wouldn't. In C, however, there is the problem of local variables. Really: case whatever: special-code...; goto common; Is a lot clearer than: case whatever: special-code...; common(a, whole, bunch, of, random, arguments); break; -- Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation. Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-' 'U` Quote: Structured Programming is a discipline -- not a straitjacket.
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/10/89)
In article <154@nmtvax.nmt.edu>, dwho@nmtvax.nmt.edu (David Olix) writes: [ how to avoid goto with a flag ] | case_matched = 1; | switch (format_char) | { | case 'd': the stuff I put under d; break; | case 'o': the stuff I put under o; break; | case 'x': the stuff I put under x; break; | case 'u': the stuff I put under u; break; | default: case_matched = 0; | } | if (case_matched) | the stuff I put under donum; | I'm not really arguing whether or not to use goto's here; I'm arguing that | adding one flag (case_matched) does not a 'whole lot of flags' make. I believe the original problem had a default case also, therefore not allowing the detection of no case matched. The flag would have to be cleared in each case. I *think* that's what I posted as an example of how to do it without a goto, but my disk monitor had to do 1 massive expire over the weekend and I don't have that stuff online. There is no question that anything can be done without a goto, although I still feel that some of the things done in the quest of avoiding gotos result in equally obscure code. When I first became convinced that structured was the way to go (and about all we had in those days was Algol-60), I did stuff like that. I'm more likely to rethink and avoid both the flags and the goto these days, but I no longer feel that a goto is a religious issue. Just a note: use of goto in net software. B news P/L 17, 91 gotos in 22k lines, one macro expands to goto rn 124 in 22k lines C news 3 in 28k lines I conclude that other people find goto useful once in a while, too. -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon
ggg@sunquest.UUCP (Guy Greenwald) (10/13/89)
In article <6490@ficc.uu.net|, peter@ficc.uu.net (Peter da Silva) writes: | The referenced article discusses replacing the Goto with a subroutine call. | The question is raised... why would that reduce clarity? | | In a block-structured language it wouldn't. In C, however, there is the | problem of local variables. | | Really: | case whatever: | special-code...; | goto common; | | Is a lot clearer than: | case whatever: | special-code...; | common(a, whole, bunch, of, random, arguments); | break; | -- | Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation | Quote: Structured Programming is a discipline -- not a straitjacket. If the arguments are really random, how can the function work? They can't just be chosen at random. As to their number, it depends on how many arguments the function needs. Regarding local variables, you know very well that C can have external variables. I think that no one is going to win this argument against you because you're going to come up with contrived rebuttals no matter what anyone says. If goto's have a place in C, you haven't made a "case" for them. /* Really: */ case whatever: special-code...; goto common; /* Is no clearer than: */ case whatever: special-code...; common(); break; --G. Guy Greenwald II
jlg@lanl.gov (Jim Giles) (10/13/89)
From article <566@sunquest.UUCP>, by ggg@sunquest.UUCP (Guy Greenwald): > /* Really: */ > case whatever: > special-code...; > goto common; > /* Is no clearer than: */ /* But it IS _MUCH_ more efficient than: */ > case whatever: > special-code...; > common(); > break; Also, which one is clearer depends on the context. I've seen a lot of code in which the GOTO version is much clearer because it doesn't separate the common code from the rest by a factor of several pages. In any event, the GOTO may be imperative for the efficiency alone. And you can't necessarily do better than the GOTO - even if you have inline procedures. Part of your efficiency requirement may be to minimize the size of the code.
chris@mimsy.UUCP (Chris Torek) (10/13/89)
In many articles many people write preantepenultimate words on `goto' when handling common code sequences with different leadins in switch statements, specifically in a `printf' format interpreter. If you all want to argue about something this pointless, you might as well agree on the base code first, so here is _doprnt (in antepenultimate form :-) i.e., not final, and untested). /* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "%W% (Berkeley) %G%"; #endif /* LIBC_SCCS and not lint */ #include <sys/types.h> #include <stdio.h> #include <varargs.h> #include <ctype.h> /* * Define FLOATING_POINT to get floating point. * * Define STDIO to get a stdio-specific version. Otherwise this * just calls putc (and #undef's it so that it will get a real function). */ #define STDIO #define FLOATING_POINT #ifdef STDIO #include "stdiom.h" #else #undef putc #endif #ifdef FLOATING_POINT /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ #define MAXEXP 308 /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ #define MAXFRACT 39 #define DEFPREC 6 #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ static int cvt(); #else #define BUF 40 #endif /* to extend shorts properly, we need signed and unsigned versions */ #define SARG() \ _ulong = flags&LONGINT ? va_arg(argp, long) : \ flags&SHORTINT ? (long)(short)va_arg(argp, int) : \ (long)va_arg(argp, int) #define UARG() \ _ulong = flags&LONGINT ? va_arg(argp, u_long) : \ flags&SHORTINT ? (u_long)(u_short)va_arg(argp, int) : \ (u_long)va_arg(argp, u_int) #define todigit(c) ((c) - '0') #define tochar(n) ((n) + '0') #define LONGINT 0x01 /* long integer */ #define LONGDBL 0x02 /* long double; unimplemented */ #define SHORTINT 0x04 /* short integer */ #define ALT 0x08 /* alternate form */ #define LADJUST 0x10 /* left adjustment */ #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ _doprnt(fmt0, argp, fp) u_char *fmt0; va_list argp; register FILE *fp; { register u_char *fmt; /* format string */ register int ch; /* character from fmt */ register int cnt; /* return value accumulator */ register int n; /* random handy integer */ register char *t; /* buffer pointer */ #ifdef FLOATING_POINT double _double; /* double precision arguments %[eEfgG] */ int fpprec; /* `extra' floating precision in [eEfgG] */ #endif u_long _ulong; /* integer arguments %[diouxX] */ int base; /* base for [diouxX] conversion */ int dprec; /* decimal precision in [diouxX] */ int fieldsz; /* field size expanded by sign, etc */ int flags; /* flags as above */ int prec; /* precision from format (%.3d), or -1 */ int realsz; /* field size expanded by decimal precision */ int size; /* size of converted field or string */ int width; /* width from format (%8d), or 0 */ char sign; /* sign prefix (' ', '+', '-', or \0) */ char softsign; /* temporary negative sign for floats */ char *digs; /* digits for [diouxX] conversion */ char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ #if defined(STDIO) && !defined(lint) /* variant of putc that does NOT handle line buffered newlines */ #define PUTC(c) \ (--fp->_w >= 0 || fp->_flags&_IOLBF && fp->_w > fp->_bf._size ? \ *fp->_p = c, fp->_p++, 0 : __putc((int)c, fp)) /* * Make sure we can write. A side effect is that _IOLBF is set * if it would get set by __putc. */ if (cantwrite(fp)) return (EOF); #else /* not STDIO or lint */ #define PUTC(c) (void) putc(c, fp) #endif fmt = fmt0; digs = "0123456789abcdef"; for (cnt = 0;; ++fmt) { #ifdef STDIO /* * Copy characters until % or \0. */ n = fp->_w, t = (char *)fp->_p; if (fp->_flags & _IOLBF) { for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) if (--n > fp->_bf._size && ch != '\n') *t++ = ch; else { fp->_w = n, fp->_p = (u_char *)t; (void) __putc(ch, fp); n = fp->_w, t = (char *)fp->_p; } } else { for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) if (--n >= 0) *t++ = ch; else { fp->_w = n, fp->_p = (u_char *)t; (void) __putc(ch, fp); n = fp->_w, t = (char *)fp->_p; } } fp->_w = n, fp->_p = (u_char *)t; #else /* STDIO */ for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) (void) putc(ch, fp); #endif /* STDIO */ if (!ch) return (cnt); flags = 0; dprec = 0; #ifdef FLOATING_POINT fpprec = 0; #endif width = 0; prec = -1; sign = '\0'; rflag: switch (*++fmt) { case ' ': /* * ``If the space and + flags both appear, the space * flag will be ignored.'' * -- ANSI X3J11 */ if (!sign) sign = ' '; goto rflag; case '#': flags |= ALT; goto rflag; case '*': /* * ``A negative field width argument is taken as a * - flag followed by a positive field width.'' * -- ANSI X3J11 * They don't exclude field widths read from args. */ if ((width = va_arg(argp, int)) >= 0) goto rflag; width = -width; /* FALLTHROUGH */ case '-': flags |= LADJUST; goto rflag; case '+': sign = '+'; goto rflag; case '.': if (*++fmt == '*') n = va_arg(argp, int); else { n = 0; while (isascii(*fmt) && isdigit(*fmt)) n = 10 * n + todigit(*fmt++); --fmt; } prec = n < 0 ? -1 : n; goto rflag; case '0': /* * ``Note that 0 is taken as a flag, not as the * beginning of a field width.'' * -- ANSI X3J11 */ flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { n = 10 * n + todigit(*fmt); } while (isascii(*++fmt) && isdigit(*fmt)); width = n; --fmt; goto rflag; #ifdef FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': flags |= SHORTINT; goto rflag; case 'l': flags |= LONGINT; goto rflag; case 'c': *(t = buf) = va_arg(argp, int); size = 1; sign = '\0'; break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': SARG(); if ((long)_ulong < 0) { _ulong = -_ulong; sign = '-'; } base = 10; goto number; #ifdef FLOATING_POINT case 'e': case 'E': case 'f': case 'g': case 'G': _double = va_arg(argp, double); /* * don't do unrealistic precision; just pad it with * zeroes later, so buffer size stays rational. */ if (prec > MAXFRACT) { if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) fpprec = prec - MAXFRACT; prec = MAXFRACT; } else if (prec == -1) prec = DEFPREC; /* * softsign avoids negative 0 if _double is < 0 and * no significant digits will be shown */ if (_double < 0) { softsign = '-'; _double = -_double; } else softsign = 0; /* * cvt may have to round up past the "start" of the * buffer, i.e. ``intf("%.2f", (double)9.999);''; * if the first char isn't NULL, it did. */ *buf = NULL; size = cvt(_double, prec, flags, &softsign, *fmt, buf, buf + sizeof(buf)); if (softsign) sign = '-'; t = *buf ? buf : buf + 1; break; #endif /* FLOATING_POINT */ case 'n': if (flags & LONGINT) *va_arg(argp, long *) = cnt; else if (flags & SHORTINT) *va_arg(argp, short *) = cnt; else *va_arg(argp, int *) = cnt; continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': UARG(); base = 8; goto nosign; case 'p': /* * ``The argument shall be a pointer to void. The * value of the pointer is converted to a sequence * of printable characters, in an implementation- * defined manner.'' * -- ANSI X3J11 */ /* NOSTRICT */ _ulong = (u_long)va_arg(argp, void *); base = 16; goto nosign; case 's': if (!(t = va_arg(argp, char *))) t = "(null)"; if (prec >= 0) { /* * can't use strlen; can only look for the * NUL in the first `prec' characters, and * strlen() will go further. */ char *p, *memchr(); if (p = memchr(t, 0, prec)) { size = p - t; if (size > prec) size = prec; } else size = prec; } else size = strlen(t); sign = '\0'; break; case 'U': flags |= LONGINT; /*FALLTHROUGH*/ case 'u': UARG(); base = 10; goto nosign; case 'X': digs = "0123456789ABCDEF"; /* FALLTHROUGH */ case 'x': UARG(); base = 16; /* leading 0x/X only if non-zero */ if (flags & ALT && _ulong != 0) flags |= HEXPREFIX; /* unsigned conversions */ nosign: sign = '\0'; /* * ``... diouXx conversions ... if a precision is * specified, the 0 flag will be ignored.'' * -- ANSI X3J11 */ number: if ((dprec = prec) >= 0) flags &= ~ZEROPAD; /* * ``The result of converting a zero value with an * explicit precision of zero is no characters.'' * -- ANSI X3J11 */ t = buf + BUF; if (_ulong != 0 || prec != 0) { do { *--t = digs[_ulong % base]; _ulong /= base; } while (_ulong); digs = "0123456789abcdef"; if (flags & ALT && base == 8 && *t != '0') *--t = '0'; /* octal leading 0 */ } size = buf + BUF - t; break; case '\0': /* "%?" prints ?, unless ? is NUL */ return (cnt); default: (void) putc((int)*fmt, fp); cnt++; continue; /* no other output */ } /* * All reasonable formats wind up here. At this point, * `t' points to a string which (if not flags&LADJUST) * should be padded out to `width' places. If * flags&ZEROPAD, it should first be prefixed by any * sign or other prefix; otherwise, it should be blank * padded before the prefix is emitted. After any * left-hand padding and prefixing, emit zeroes * required by a decimal [diouxX] precision, then print * the string proper, then emit zeroes required by any * leftover floating precision; finally, if LADJUST, * pad with blanks. */ /* * compute actual size, so we know how much to pad * fieldsz excludes decimal prec; realsz includes it */ #ifdef FLOATING_POINT fieldsz = size + fpprec; #else fieldsz = size; #endif if (sign) fieldsz++; if (flags & HEXPREFIX) fieldsz += 2; realsz = dprec > fieldsz ? dprec : fieldsz; /* right-adjusting blank padding */ if ((flags & (LADJUST|ZEROPAD)) == 0 && width) for (n = realsz; n < width; n++) PUTC(' '); /* prefix */ if (sign) PUTC(sign); if (flags & HEXPREFIX) { PUTC('0'); PUTC((int)*fmt); } /* right-adjusting zero padding */ if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) for (n = realsz; n < width; n++) PUTC('0'); /* leading zeroes from decimal precision */ for (n = fieldsz; n < dprec; n++) PUTC('0'); /* the string or number proper */ n = size; #ifdef STDIO if (fp->_flags & _IOLBF) while (--n >= 0) { ch = *t++; if (--fp->_w > fp->_bf._size && ch != '\n') *fp->_p++ = ch; else (void) __putc(ch, fp); } else while (n) { if ((ch = fp->_w) > n) ch = n; if (ch) { bcopy((void *)fp->_p, (void *)t, ch); fp->_w -= ch; fp->_p += ch; n -= ch; } else { (void) __putc(*t++, fp); n--; } } #else /* STDIO */ while (--n >= 0) (void) putc(*t++, fp); #endif /* STDIO */ #ifdef FLOATING_POINT /* trailing f.p. zeroes */ while (--fpprec >= 0) PUTC('0'); #endif /* left-adjusting padding (always blank) */ if (flags & LADJUST) for (n = realsz; n < width; n++) PUTC(' '); /* finally, adjust cnt */ cnt += width > realsz ? width : realsz; } /* NOTREACHED */ } #ifdef FLOATING_POINT static char *exponent(); static char *round(); static cvt(number, prec, flags, signp, fmtch, startp, endp) double number; register int prec; int flags; u_char fmtch; char *signp, *startp, *endp; { register char *p, *t; register double fract; int dotrim, expcnt, gformat; double integer, tmp, modf(); dotrim = expcnt = gformat = 0; fract = modf(number, &integer); /* get an extra slot for rounding. */ t = ++startp; /* * get integer portion of number; put into the end of the buffer; the * .01 is added for modf(356.0 / 10, &integer) returning .59999999... */ for (p = endp - 1; integer; ++expcnt) { tmp = modf(integer / 10, &integer); *p-- = tochar((int)((tmp + .01) * 10)); } switch(fmtch) { case 'f': /* reverse integer into beginning of buffer */ if (expcnt) for (; ++p < endp; *t++ = *p); else *t++ = '0'; /* * if precision required or alternate flag set, add in a * decimal point. */ if (prec || flags&ALT) *t++ = '.'; /* if requires more precision and some fraction left */ if (fract) { if (prec) do { fract = modf(fract * 10, &tmp); *t++ = tochar((int)tmp); } while (--prec && fract); if (fract) startp = round(fract, (int *)NULL, startp, t - 1, (char)0, signp); } for (; prec--; *t++ = '0'); break; case 'e': case 'E': eformat: if (expcnt) { *t++ = *++p; if (prec || flags&ALT) *t++ = '.'; /* if requires more precision and some integer left */ for (; prec && ++p < endp; --prec) *t++ = *p; /* * if done precision and more of the integer component, * round using it; adjust fract so we don't re-round * later. */ if (!prec && ++p < endp) { fract = 0; startp = round((double)0, &expcnt, startp, t - 1, *p, signp); } /* adjust expcnt for digit in front of decimal */ --expcnt; } /* until first fractional digit, decrement exponent */ else if (fract) { /* adjust expcnt for digit in front of decimal */ for (expcnt = -1;; --expcnt) { fract = modf(fract * 10, &tmp); if (tmp) break; } *t++ = tochar((int)tmp); if (prec || flags&ALT) *t++ = '.'; } else { *t++ = '0'; if (prec || flags&ALT) *t++ = '.'; } /* if requires more precision and some fraction left */ if (fract) { if (prec) do { fract = modf(fract * 10, &tmp); *t++ = tochar((int)tmp); } while (--prec && fract); if (fract) startp = round(fract, &expcnt, startp, t - 1, (char)0, signp); } /* if requires more precision */ for (; prec--; *t++ = '0'); /* unless alternate flag, trim any g/G format trailing 0's */ if (gformat && !(flags&ALT)) { while (t > startp && *--t == '0'); if (*t == '.') --t; ++t; } t = exponent(t, expcnt, fmtch); break; case 'g': case 'G': /* a precision of 0 is treated as a precision of 1. */ if (!prec) ++prec; /* * ``The style used depends on the value converted; style e * will be used only if the exponent resulting from the * conversion is less than -4 or greater than the precision.'' * -- ANSI X3J11 */ if (expcnt > prec || !expcnt && fract && fract < .0001) { /* * g/G format counts "significant digits, not digits of * precision; for the e/E format, this just causes an * off-by-one problem, i.e. g/G considers the digit * before the decimal point significant and e/E doesn't * count it as precision. */ --prec; fmtch -= 2; /* G->E, g->e */ gformat = 1; goto eformat; } /* * reverse integer into beginning of buffer, * note, decrement precision */ if (expcnt) for (; ++p < endp; *t++ = *p, --prec); else *t++ = '0'; /* * if precision required or alternate flag set, add in a * decimal point. If no digits yet, add in leading 0. */ if (prec || flags&ALT) { dotrim = 1; *t++ = '.'; } else dotrim = 0; /* if requires more precision and some fraction left */ if (fract) { if (prec) { do { fract = modf(fract * 10, &tmp); *t++ = tochar((int)tmp); } while(!tmp); while (--prec && fract) { fract = modf(fract * 10, &tmp); *t++ = tochar((int)tmp); } } if (fract) startp = round(fract, (int *)NULL, startp, t - 1, (char)0, signp); } /* alternate format, adds 0's for precision, else trim 0's */ if (flags&ALT) for (; prec--; *t++ = '0'); else if (dotrim) { while (t > startp && *--t == '0'); if (*t != '.') ++t; } } return(t - startp); } static char * round(fract, exp, start, end, ch, signp) double fract; int *exp; register char *start, *end; char ch, *signp; { double tmp; if (fract) (void)modf(fract * 10, &tmp); else tmp = todigit(ch); if (tmp > 4) for (;; --end) { if (*end == '.') --end; if (++*end <= '9') break; *end = '0'; if (end == start) { if (exp) { /* e/E; increment exponent */ *end = '1'; ++*exp; } else { /* f; add extra digit */ *--end = '1'; --start; } break; } } /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ else if (*signp == '-') for (;; --end) { if (*end == '.') --end; if (*end != '0') break; if (end == start) *signp = 0; } return(start); } static char * exponent(p, exp, fmtch) register char *p; register int exp; u_char fmtch; { register char *t; char expbuf[MAXEXP]; *p++ = fmtch; if (exp < 0) { exp = -exp; *p++ = '-'; } else *p++ = '+'; t = expbuf + MAXEXP; if (exp > 9) { do { *--t = tochar(exp % 10); } while ((exp /= 10) > 9); *--t = tochar(exp); for (; t < expbuf + MAXEXP; *p++ = *t++); } else { *p++ = '0'; *p++ = tochar(exp); } return(p); } #endif /* FLOATING_POINT */ -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris
dhesi@sunscreen.UUCP (Rahul Dhesi) (10/13/89)
In article <566@sunquest.UUCP> ggg@sunquest.UUCP (Guy Greenwald) writes: >/* Really: */ > case whatever: > special-code...; > goto common; >/* Is no clearer than: */ > case whatever: > special-code...; > common(); > break; Possible counter-example: int counterexample(...) { ... other stuff ... switch (yyy) { case x: ... other stuff ... goto err_ret; case y: ... other stuff ... goto err_ret; case other-stuff: ... other stuff ... break; ... } return 0; /* no need to close file; return success */ common: errno = last_error;/* save latest error for caller's sake */ if (last_error != CE_NOFILE) fclose(f); /* close file if still open */ return -1; /* return error code */ } /* counterexample() */ Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com> UUCP: oliveb!cirrusl!dhesi
cik@l.cc.purdue.edu (Herman Rubin) (10/13/89)
In article <566@sunquest.UUCP>, ggg@sunquest.UUCP (Guy Greenwald) writes: < In article <6490@ficc.uu.net|, peter@ficc.uu.net (Peter da Silva) writes: ..................... < | Really: < | case whatever: < | special-code...; < | goto common; < | < | Is a lot clearer than: < | case whatever: < | special-code...; < | common(a, whole, bunch, of, random, arguments); < | break; < | -- < | Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation < | Quote: Structured Programming is a discipline -- not a straitjacket. > > If the arguments are really random, how can the function work? They > can't just be chosen at random. As to their number, it depends on how > many arguments the function needs. Regarding local variables, you know > very well that C can have external variables. I think that no one is going > to win this argument against you because you're going to come up with > contrived rebuttals no matter what anyone says. If goto's have a place > in C, you haven't made a "case" for them. > > /* Really: */ > case whatever: > special-code...; > goto common; > /* Is no clearer than: */ > case whatever: > special-code...; > common(); > break; One problem with the function call is that, as da Silva writes, there can be a lot of other variables being used. It can be quite difficult to manage the implicit passage of arguments that Greenwald is assuming away. There are other problems. The cost of a subroutine call is necessarily considerably greater than that of a transfer. Furthermore, the first block of code indicates that, at the termination of common, the program continues from that point and does not do a return to the place from which it is called. I find this rather a common situation, and have not seen an alternative to the goto for this situation which is not horribly expensive and less clear. Also, at the continuation, the "whole bunch of random arguments" is still there for the rest of the program to use (or use as modified by common). This is not provided for by a call. -- Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Phone: (317)494-6054 hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)
peter@ficc.uu.net (Peter da Silva) (10/13/89)
I said, in an example, "common(a, whole, bunch, of, random, arguments);"... In article <566@sunquest.UUCP> ggg@sunquest.UUCP (Guy Greenwald) writes: > If the arguments are really random, how can the function work? [ A whole lot of pedantic stuff ] > Regarding local variables, you know > very well that C can have external variables. I can be pedantic too. Local variables, properly used, add considerably to the clarity and maintainability of a program. Data hiding is acknowledged as a good thing, because it increases the locality of the information required to understand a block of code, and ensures that unsuspected back doors are not created by careless programmers. In fact, one of the main reasons for following the discipline of structured programming is to improve this locality, and hide control structures from each other. If on the one hand you improve the structure of control flow, by eliminating gotos, and on the other degrade the structure of your data, by adding a bunch of global (or even file-local) variables, what have you really gained? > I think that no one is going > to win this argument against you because you're going to come up with > contrived rebuttals no matter what anyone says. Well would you care to address the merits of my argument rather than either coming up with contrived examples of your own or by blindly restating religious doctrine? To rebut your rebuttal of my contrived example, what about re-entrant routines? -- Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation. Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-' 'U` Quote: Structured Programming is a discipline -- not a straitjacket.
peter@ficc.uu.net (Peter da Silva) (10/15/89)
Well, that's not the version of _doprnt I remember. -- Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation. Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-' 'U` Quote: Structured Programming is a discipline -- not a straitjacket.
greyfox@agtoa.UUCP (Bruce Ide) (10/24/89)
Basic programmers use GOTO's excessively. I usually never use any, but if you are nested four loops deep and need to get all the way out, you can either use a GOTO or set a condition and test it four times. In cases such as this, the GOTO is the Logical Choice. The examples I have seen here: >case 1: > command(); > break; as opposed to >case 1: > goto command; > break; I wouldn't use a GOTO here. Putting a goto here would be something a BASIC programmer would do. When your program gets > 20K or so, keeping track of global variables and GOTO's is a MAJOR pain. You'll know that when the guy who has to maintain it after you're gone comes after you with a shot gun. (I like local arrays of structures and pointers to functions too. Keeps the code portable :-) -Bruce Ide (Grey Fox) uunet!agtoa!greyfox