Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

uwbadger

macrumors member
May 20, 2009
69
21

You're missing the point. I've read lots of Dijkstra. I know the arguments. But the person who said that they weren't practicing a superstition failed to provide any solid reason for avoiding gotos.

Other than the points Dijkstra presents, in practical usage in reading code written in procedural languages go to can completely obliterate your reasoning about the flow of a program. At any point execution might jump to any label. You were in a loop, now you aren't. You were counting coins in a jar, you made it to $1.30, now you're shearing a sheep. What happened?

Sure, and that's why I said that gotos should usually be avoided. When I use gotos, it's only to jump forward (hence no spaghetti code) and only for error-handling and clean-up. (Of the roughly 10k gotos in gcc, I'm responsible for perhaps 5.)

As for the source file that's been posted by Knuth... all I can say is he was unfortunate enough to be converting from FORTRAN (all caps means a version prior to 77, which the comments mention is the year the code was completed). By far the worst code I've read has been fortran old enough to be referred to as FORTRAN.

-Lee

For what it's worth, Knuth has long been an advocate of using gotos in the appropriate circumstances:

http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf
 

AppleMacFinder

macrumors 6502a
Dec 7, 2009
796
152
Warning: if you use goto too much, you will get used to it.
And programs with a lot of gotos are harder for debug.
However, goto is useful with assembler and Pascal.
 

ulbador

macrumors 68000
Feb 11, 2010
1,554
0
Haven't any of you ever programmed on a TI-83 before?

/me remembers the days of GOTO

I did TI-85 programming. Back then, you could use TI-Basic, but it was VERY limited compared to the Z80 assembly. I fried the crap out of at least two of these trying to hack them to do various things (overclocking and everything else).
 

Sydde

macrumors 68030
Aug 17, 2009
2,556
7,057
IOKWARDI
You say it's not superstition, but you don't mention a reason for avoiding gotos!

Ok, my main point: when you use the natural devices to give your code structure, they provide you with explicit clues that help you read the structure. Indent notwithstanding (because you could easily indent your goto-laden code manually), when you see a "{" or a "BEGIN" (Pascal), a "END" or a "}", you instantly understand where to look for the body of code that interests you. When you implement structure with "goto", just looking at that line gives you nothing (though, yes, the name of the label might help, but label naming conventions must be implemented by the coder). Avoiding goto gives your code a clear and consistent structure and, if you are hard-nosed, forces you into consistent programming patterns. In addition, the lack of illuminating label names encourages you to comment your code more thoroughly (if you want to be able to maintain it).

Now, take a look at the code in the OP:
Code:
#include <stdio.h>

main ()

{
	int remainder, numerator, denominator ;
	printf ("This program computes the remainder of user entered numbers," ) ;
	printf ("0 to exit\n") ;
	
	do {
	printf ("Enter a number: ") ;
	scanf ("%d", &numerator) ;
	if (numerator == 0) goto done;
	printf ("Enter a number: ") ;
	scanf ("%d", &denominator) ;
	
	remainder = numerator % denominator ;
	
	
	printf ("%d\n", remainder) ;
	} while (numerator != 0); 
	
	[B][COLOR="Red"]done[/COLOR][/B]: printf ("Thank you, have a nice day\n") ;
	
	return 0;
	
}

Note the location of the exit label. Now, when I see a piece of structure like a loop, I see that it ends with the "}", and (apart from the trailing "while" clause in this example) there is nothing after that. Hence, if I want to insert code after the loop, I will instinctively add it before the "done:" label, which is rather obviously not where such an insertion should be made.

In other words, you could easily design and structure any fully-functional program completely out of gotos and labels ("if" would be the only other flow control), though I suspect it would probably be a major PITA to debug to workable and then subsequently maintain. Alternately, you could completely eschew the goto concept and rely entirely on the language's natural structural components, in which case the code would almost certainly be easier to read and maintain. As shown in the example above, mixing goto with natural structure is dangerous and confusing practice. There may in fact be times when you really do need to do that, but if it comes to that, the routine in question ought to be 10% lines-of-code and 90% //documentation, just to be on the safe side.

(Personally, I do not even like to have more than one return statement in a routine.)
 
Last edited:

chmilar

macrumors member
Sep 25, 2003
49
0
Code:
#include <stdio.h>

main ()
{
    int numerator, denominator;
    printf ("This program computes the remainder of user entered numbers," );
    printf ("0 to exit\n");
	
    for ( ;; ) {
        printf ("Enter numerator: ");
        scanf ("%d", &numerator);
        if (numerator == 0) {
            printf ("Thank you, have a nice day\n");
            return 0;
        }

        printf ("Enter denominator: ");
        scanf ("%d", &denominator);
        if (denominator != 0)
            printf ("%d\n", numerator % denominator);
        else
            printf("0 denominator yields undefined result\n");
    }
}

There is no need for goto, break, continue, multiple redundant tests of numerator, or the remainder variable.
 

mydogisbox

macrumors member
Jan 16, 2011
64
0
In general I don't think that burying the exit condition in the middle of the loop makes anything more apparent. At first glance, I would expect your preferred loop/goto version to access and test all 1000 data points.

I think you're seeing us as being farther apart than we actually are. I would not (ECK!) bury a goto in the middle of a section of code. The only place that I would use it is at the very end of the inside loop for the purpose of cleanly (as-in, without having to muddle the looping condition for each of my loops) exit nested loops. I additionally support not having multiple returns in a function call with the exception of error handling ( if(null)return; ). I don't like using goto, but that is more of a personal issue than a well-thought out position and as such I use it in a few, very select, cases.
 

firewood

macrumors G3
Jul 29, 2003
8,113
1,353
Silicon Valley
On the other hand, Knuth uses gotos much more liberally than I do...

+1 for mentioning Knuth.

I once worked with a production quality compiler that was written in assembly language, which ran on a microcoded custom CPU (discrete TTL MSI logic). It was GOTOs all the way down.

There seem to be a few religious or politically correct tenets often espoused regarding currently recommended software practices. I learned long ago not to argue with religious or political fanatics... whether I partially agreed with them or not.
 

holmesf

macrumors 6502a
Sep 30, 2001
528
25
+1 for mentioning Knuth.

I once worked with a production quality compiler that was written in assembly language, which ran on a microcoded custom CPU (discrete TTL MSI logic). It was GOTOs all the way down.

There seem to be a few religious or politically correct tenets often espoused regarding currently recommended software practices. I learned long ago not to argue with religious or political fanatics... whether I partially agreed with them or not.

If it was written in assembly language of course it was "gotos", ie jump instructions. It means it also manually pushed and popped arguments on/off the stack. Does that mean one should do these things in a high level language? Absolutely not. It's not a religious issue at all as the consensus is pretty clear that one should not use gotos except in outstanding circumstances.
 

balamw

Moderator emeritus
Aug 16, 2005
19,366
979
New England
I would not (ECK!) bury a goto in the middle of a section of code. The only place that I would use it is at the very end of the inside loop for the purpose of cleanly (as-in, without having to muddle the looping condition for each of my loops) exit nested loops.

If it crosses {} lines I would consider it buried ;), but as you say this is a personal preference and is not a religious doctrine.

In the context of these threads that are mainly directed at folks who are really just starting out, I really think the slippery slope argument that AppleMacFinder raises is the main issue I am concerned about. Without enough warning and examples where its use can be avoided, newbie programmers of today will do just like their counterparts in the past and write poorly structured code for no other reason than they can.

B
 

mydogisbox

macrumors member
Jan 16, 2011
64
0
If it crosses {} lines I would consider it buried ;), but as you say this is a personal preference and is not a religious doctrine.

In the context of these threads that are mainly directed at folks who are really just starting out, I really think the slippery slope argument that AppleMacFinder raises is the main issue I am concerned about. Without enough warning and examples where its use can be avoided, newbie programmers of today will do just like their counterparts in the past and write poorly structured code for no other reason than they can.

B

That's hard to disagree with. Maybe there should be some requirement that new programmers have to read a certain number of lines of code written entirely using gotos and then an equivalent section of code written using better practice so that they know WHY people are so anti-goto. That way they can make informed decisions.
 

Sydde

macrumors 68030
Aug 17, 2009
2,556
7,057
IOKWARDI
That's hard to disagree with. Maybe there should be some requirement that new programmers have to read a certain number of lines of code written entirely using gotos and then an equivalent section of code written using better practice so that they know WHY people are so anti-goto. That way they can make informed decisions.

I feel the opposite. The documentation should exist in the last paragraph of appendix 0xFF. Its existence and application should be left out of the main body instructional manuals and reference books.Structural habits need to be fully established before a programmer should even be aware of this last resort.
 

balamw

Moderator emeritus
Aug 16, 2005
19,366
979
New England
Goto should be relegated to a chapter right at the end of the book that you can only get to by breaking a seal that says: "Here be dragons. Enter at your own peril".

The documentation should exist in the last paragraph of appendix 0xFF. Its existence and application should be left out of the main body instructional manuals and reference books. Structural habits need to be fully established before a programmer should even be aware of this last resort.

I think we agree, though I sealed it off. :p

B
 

Mac Player

macrumors regular
Jan 19, 2006
225
0
hmmm, you will have to provide a better example than that
Code:
foo = malloc(wada wada ....)
if ( foo != NULL ) {
    omg = malloc(another wada wada...);
    if ( omg != NULL ) {
        ...
        bananas= malloc(last wada promise);
        if ( banana != NULL ) {
            ...
            free(banana);
        }
        free(omg);
        ...
    }
    free(foo);
}
// IMO, using goto labels as an alternative for comments fails to make sense
// but indented code between the { ... } improves readability

How is it more readable? Do you keep indenting code? Do you think that tracking the clean up all over a function is more readable?
 

Sydde

macrumors 68030
Aug 17, 2009
2,556
7,057
IOKWARDI
How is it more readable? Do you keep indenting code? Do you think that tracking the clean up all over a function is more readable?

Your allocations are progressive: each one is dependent on the preceding allocation. Typically, this kind of design would do most or all of its work in the interior scope, wherein every allocation has succeeded. The trailing part shows the scopes unwinding, performing their successive deallocations. To me, that design is far more readable than using gotos, and many on this forum seem to agree with that general idea.

And yes, I do keep on indenting code. It helps me visually locate the depth of a section (and it can get very deep). With editors that allow you to easily adjust the indent width, you can make the formatting work for you.

The net result is zero. What you wrote and how I constructed it produce A very nearly identical result. I think what I wrote is more readable and easier to maintain. As balamw said earlier, having the { ... } structuring makes it much easier to convert a large routine into logical and sensible subroutines by little more than just grabbing onto the handles and carrying them elsewhere. And firewood rightly points out that working with natural structures increases the likelihood that subtle errors might get caught by the compiler.

In short, goto offers no performance advantage, a significant maintenance disadvantage, and may encourage programmers to make poor choices in design.
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
Your allocations are progressive: each one is dependent on the preceding allocation. Typically, this kind of design would do most or all of its work in the interior scope, wherein every allocation has succeeded. The trailing part shows the scopes unwinding, performing their successive deallocations. To me, that design is far more readable than using gotos, and many on this forum seem to agree with that general idea.

In short, goto offers no performance advantage, a significant maintenance disadvantage, and may encourage programmers to make poor choices in design.

I'm still not convinced. I like the primary flow to be straight down the method as much as possible. This way if you can get an overview of the method by reading all first indented lines, then you can get progressively more details by reading increasingly further indented lines.

Compare:

Code:
initialise all resource holding variables 
  to non-holding state (typically NULL);

allocate resource 1;
if (! allocated) goto cleanup;
setup resource 1;
if (! resource 1 is in good state) goto cleanup;

/* etc. */

allocate resource n;
if (! allocated) goto cleanup;
setup resource n;
if (! resource n is in good state) goto cleanup;


/* do stuff */
if (! stuff successful) goto cleanup;
/* do more stuff */
if (! more stuff successful) goto cleanup;
/* do rest of stuff */


cleanup:

if (resource n allocated) {
  free resource n;
}

/* etc. */

if (resource 1 allocated) {
  free resource n;
}

verses:

Code:
allocate resource 1;
if (allocated) {
  setup resource 1;
  if (resource 1 is in good state) {

    /* etc. */

                   allocate resource n;
                   if (allocated) {
                     setup resource n;
                     if (resource n is in good state) {

                       /* do stuff */
                       if (stuff successful) {
                         /* do more stuff */
                         if (more stuff successful) {
                           /* do rest of stuff */
                         }
                       }
                        
                     }
                     free resource n;
                   }

     /* etc. */

  }
  free resource 1;
}

Now consider maintenance of these two code styles. Considered adding a new resource. Considered doing more stuff that can fail.
 

Sydde

macrumors 68030
Aug 17, 2009
2,556
7,057
IOKWARDI
Now consider maintenance of these two code styles. Considered adding a new resource. Considered doing more stuff that can fail.

I guess it is a good thing we are not collaborating on a project. The indenting and the "{ ... }" structures are much easier for me to parse. Further to the right does not make source harder to understand, nor does the leftward cascade of unwinding hamper comprehensibility. (Note also that your cleanup routine has conditionals that are missing in the second example.)

I personally do not like flat, linear code because, to me that is poor style. Using natural structure divides a process into its natural structure, which is somewhat less evident when gotos and labels are used. And the natural structure makes refactoring easier.

Consider adding a new resource: insert
Code:
rsrc3 = create_a_resource();
if ( rsrc3 != NULL ) {
    doSomeStuff();
    ...
    deallocate( rsrc3 );
}
Note how the new resource is nicely contained in the { ... } (after if is known to be good). Now, over yonder, you need something that does exactly this: how handy, it is all right there, the entire lifespan of rsrc3 is nicely laid out for you to cut and paste. How much easier do you need it to be?

And as I said elsewhere, you can set a single space indent if you want.


Now you will excuse me, my bicycle forum is debating the merits of helmet use...
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
Now you will excuse me, my bicycle forum is debating the merits of helmet use...

LOL :D

The conditionals around the resource freeing aren't necessary in the blocked-style because the free never gets called if the resource is never allocated.

I think I started with blocked-style. But when I moved on to languages with try...finally I got into the habit of think of manual resource management this way. When I came back to C, I started using the goto-style to implement the missing try...finally.
 

firewood

macrumors G3
Jul 29, 2003
8,113
1,353
Silicon Valley
Maybe there should be some requirement that new programmers have to read a certain number of lines of code written entirely using gotos and then an equivalent section of code written using better practice ...

Fail, for a straw man argument that assumes the conclusion. Why not say that all structured coding is bad after being forced to write large applications entirely using only while statements for flow control?
 

mydogisbox

macrumors member
Jan 16, 2011
64
0
Fail, for a straw man argument that assumes the conclusion. Why not say that all structured coding is bad after being forced to write large applications entirely using only while statements for flow control?

Wow! Talk about setting up a straw man! I wasn't even making an argument! I was merely pointing out that the use of gotos would be improved by learning programmers if they saw examples of why people feel the way they do about gotos! Maybe you should get off your high horse for a bit and actually try to understand what other people are trying to say.
 

Sydde

macrumors 68030
Aug 17, 2009
2,556
7,057
IOKWARDI
The conditionals around the resource freeing aren't necessary in the blocked-style because the free never gets called if the resource is never allocated.

I think I started with blocked-style. But when I moved on to languages with try...finally I got into the habit of think of manual resource management this way. When I came back to C, I started using the goto-style to implement the missing try...finally.

I must confess that resource allocation has been greatly simplified for me with Cocoa and its NSAutoreleasePool. Things often evaporate at some other time when I am not worrying about them.

But my main point is this: if you do some stuff that goes together, all that stuff should go together. In your example, you have creation and use (conditional) in one spot and deallocation in another. If I want to add to the structure, using the goto scheme, I have to make changes in two different places, whereas with blocked structure, all the related things go into a coherent package. Unless there is a very good reason to divide things up, I will keep them together, because making changes in various locations is a recipe fir forgetting what you are doing. (Oops, phone rang ... good night Gracie ... now WTH was I?)
 

firewood

macrumors G3
Jul 29, 2003
8,113
1,353
Silicon Valley
I was merely pointing out that the use of gotos would be improved by learning programmers if they saw examples of why people feel the way they do about gotos!

Agreed. If student programmers read lots of elegant code that used a few gotos in easy-to-read and provably correct design patterns, they probably would write code with more gotos.

Read some vintage Knuth.
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
I must confess that resource allocation has been greatly simplified for me with Cocoa and its NSAutoreleasePool.

Absolutely true. And with the Mac (and I'm guessing iOS one day) going GC, this is less and less of an issue, assuming someone doesn't fall into the trap of using GC to manage critical resources.

But my main point is this: if you do some stuff that goes together, all that stuff should go together.

This is a good point. But I don't see how you don't have to do it in two places in either style. In both styles, if you add a resource, you need to allocate it at the top and remember to deallocate at the bottom.

As an aside, I think C++'s style of allocation in constructors and deallocation in destructors with stack-based objects is the most elegant, coder-friendly solution I've experienced.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.