gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > What's the deal with the "goto" command?

#19168 - Krakken - Tue Apr 13, 2004 4:11 am

When I was first learning C programming all books that I read strongly recommended the sparse use of goto commands. However, I don't see why this would be as I can see many uses that just would be a lot more difficult to implement otherwise.

Can anyone please tell me why everyone hates this command so much?

#19169 - Pinski - Tue Apr 13, 2004 4:20 am

It's not very good code to use the Goto command. You can use it, but mostly it's frowned upon. Most times you could use the goto command you can find another way to do the same thing with either a loop or a function call. The main problem with Goto statements is that it makes it hard to read other people's code, it's called spaghetti code. Imagine a code like this:


A - bunch of code
B - goto E
C - some code that goes after A and D, but before G
D - goto G
E - some code that is processed after A, but before C and G
F - goto C
G - more code here

That kind of sloppy use of code makes it hard to debug and follow for others that work on your code. I've heard of Professors talking about doing maintenance on systems that have over 30-50 pages of code that was written like that back in the day. Could you image the time that it would take to straighten the code out? That's why people don't like goto statements unless they are positively necessary.

#19170 - yaustar - Tue Apr 13, 2004 4:30 am

I think it is also something to do with the flow of code. C is an imperative language and is a top to bottom flow and goto breaks this flow. It was used mostly in BASIC because you couldn't code any individual procedures (correct me here, my knowledge of BASIC is very low here), in C you can.
_________________
[Blog] [Portfolio]

#19171 - poslundc - Tue Apr 13, 2004 5:29 am

Goto precedes BASIC, but is perhaps most widely known in that language.

Goto is a very primitive command/concept: pick another spot in the code and branch to it. Ultimately, all of the loops, function calls, if-blocks, etc. you put into programs are translated into "gotos".

Modern programming theory is strongly based around ideas of encapsulation and modularization, and through various constructs such as loops, flow-control, procedures and functions, goto has essentially been made obsolete: for any situation where you might use a goto statement, there is almost always a more sophisticated and elegant construct that can help guard against the spaghetti coding that can easily result from arbitrary branching.

What situation are you thinking of that would be difficult to implement without using goto statements?

Dan.

#19172 - LOst? - Tue Apr 13, 2004 10:05 am

poslundc wrote:

What situation are you thinking of that would be difficult to implement without using goto statements?

Dan.


When being inside of an interrupt routine, you sometimes have to use goto for faster branches to the end of the routine to set the interrupt flag and do end coding. Having a subroutine call eats the cycles.
You save time by using goto when leaving a "for" loop in a hurry. "for" loops have the "continue" and "next" but they don't have "break".

#19176 - Sweex - Tue Apr 13, 2004 12:03 pm

In all my years of pascal and C/C++ programming I never had to use goto once in all my code and to be honest; it disgusts me (for all the reasons mentioned already).

I will admit though that it took a while to get used to when I switched from basic to pascal!
_________________
If everything fails, read the manual: If even that fails, post on forum!

#19179 - f(DarkAngel) - Tue Apr 13, 2004 1:05 pm

The origin of goto statement is assembly. The structure of BASIC is similar to assembly. You'll see jumps if you disassemble for,while,do,switch,if/else if statements. It is simply what you code is going to be translated.

In other words, if you want to use label & goto (jmp/bra), try assembly.
_________________
death scream...

#19180 - torne - Tue Apr 13, 2004 1:09 pm

f(DarkAngel) wrote:
You'll see jumps if you disassemble for,while,do,switch,if/else if statements. It is simply what you code is going to be translated.


We don't want to see them; it's why we're using the high level languages in the first place. Well, I say 'we', I like assembly. =)

#19183 - poslundc - Tue Apr 13, 2004 3:40 pm

LOst? wrote:
When being inside of an interrupt routine, you sometimes have to use goto for faster branches to the end of the routine to set the interrupt flag and do end coding. Having a subroutine call eats the cycles.


You don't need a subroutine call. You can use an "if" statement, and put control braces around the section you want to skip.

Or if you prefer, use inline functions.

In fact, using a goto statement can slow you down because the compiler can't optimize the control flow of your program. If you want to be that specific about where your program branches and when for the sake of counting cycles, then you should be programming in assembly.

Quote:
You save time by using goto when leaving a "for" loop in a hurry. "for" loops have the "continue" and "next" but they don't have "break".


Uh... no?

Dan.

#19184 - jma - Tue Apr 13, 2004 4:22 pm

LOst? wrote:
"for" loops have the "continue" and "next" but they don't have "break".


Just plain wrong. Break can be used in for loops, switch/case statements and do/while loops. Goto can be extremely useful, but these instances are few and far between. Reasons why goto is frowned upon:

o Difficult to understand program flow
o You can kill the hardware stack in-advertantly
o Very difficult for a compiler to optimize

o On smaller architectures (even THUMB mode) JMP/B instructions are very limited in their range -- a longer jump would require a lot of code to work around (killing your speed advantage).

o Many more reasons...

Jeff
_________________
massung@gmail.com
http://www.retrobyte.org

#19186 - MayFly - Tue Apr 13, 2004 4:57 pm

Hi Guys,

Here's an excerpt from Steve McConnell's book "Code Complete" that talks about reasons why and when gotos are valid tools:

A well-placed goto can eliminate the need for duplicate code. Duplicate code leads to problems if the two sets of code are modified differently. Duplcate code increases the size of source and executable files. The bad effects of the goto are outweighed in such a case by the risks of duplicate code.

The goto is useful in a routine that allocates resources, performs operations on those resources, and then deallocates the resources. With a goto, you can clean up in one section of code. The goto reduces the likelihood of your forgetting to deallocate the resources in each place you detect an error.

...

Good programming doesn?t mean eliminating gotos, Methodical decomposition, refinement, and selection of control structures automatically lead to goto-free programs in most cases. Achieving gotoless code in not the aim, but the outcome, and putting the focus of avoiding gotos isn?t helpful.

Code Complete pg. 349

MayFly

#19189 - torne - Tue Apr 13, 2004 5:13 pm

MayFly wrote:
The goto is useful in a routine that allocates resources, performs operations on those resources, and then deallocates the resources. With a goto, you can clean up in one section of code. The goto reduces the likelihood of your forgetting to deallocate the resources in each place you detect an error.


My general opinion of Code Complete is pretty low, but what is being described here is an unstructured exception handler, implemented manually using goto. If you're not in a piece of code which is performance critical, and you happen to like that sort of thing, you can use structured exception handling to do it. It's a shame that C doesn't have unstructured exceptions, as this would neatly avoid this case, but this is a plausible use of goto which is difficult to dodge otherwise. One way to simulate unstructured exceptions in C without using goto that works reasonably well is to take your code that looks (logically) something like this:
Code:
try {
  if (!doStuff()) throw;
  if (!doMoreStuff()) throw;
} finally {
  cleanup();
}

and replace it with something like:
Code:
inline void tryBody() {
  if (!doStuff()) return;
  if (!doMoreStuff()) return;
}

tryBody();
cleanup();


This is a pretty simple example; where it breaks down is when the try body needs to use more than one or two values from the context it originally appeared in (as it starts to get ugly). If you needed to know whether there was an 'exception', then you would make the function a bool and return false when throwing (and true at the end). I've coded like this quite often; it looks quite clean (as if the try body is doing a large number of things anyway, it should possibly be a seperate function in the first place) and generates good code (compilers are better at handling inlined returns than gotos).

#19193 - tepples - Tue Apr 13, 2004 10:57 pm

jma wrote:
Break can be used in for loops, switch/case statements and do/while loops.

True, but unlike the break in the Java language which can jump from deep nesting within a block to the end of that block, provided you've given the block a label, the break in C and C++ can't jump out of more than one such block at a time.

Quote:
Goto can be extremely useful, but these instances are few and far between.

I would stress the difference between "few and far between" and "nonexistent".

Quote:
Reasons why goto is frowned upon:

o Difficult to understand program flow

In some cases. But in others, it's possible to implement, say, exceptions and coroutines in the C language rather cleanly in terms of macros on top of goto. However, as torne mentioned, if your try block is too big, you probably need to refactor something.

Quote:
o You can kill the hardware stack in-advertantly

We're talking goto, not setjmp and longjmp.

Quote:
o Very difficult for a compiler to optimize

Granted.

Quote:
On smaller architectures (even THUMB mode) JMP/B instructions are very limited in their range -- a longer jump would require a lot of code to work around (killing your speed advantage).

C goto works within the scope of a function, and a single function usually fits within most C friendly architectures' branch ranges; otherwise, big loops would run even more slowly.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#19198 - dagamer34 - Tue Apr 13, 2004 11:47 pm

Well, if you wanted to do some code in C and then translate it into assembly later on, goto has it's uses. But in common practice, it leads to havoc.

It's not that goto is bad, we are just lazy programmers by nature (well, some of us).
_________________
Little kids and Playstation 2's don't mix. :(

#19200 - sgeos - Wed Apr 14, 2004 12:55 am

tepples wrote:
Quote:
Goto can be extremely useful, but these instances are few and far between.

I would stress the difference between "few and far between" and "nonexistent".


Goto in C is useful for jumping out of nested loops. This case is "few and far between", not "nonexistent". Sure, any code can be restructured to avoid the use of goto. I believe that Java doesn't even have gotos, and the java programmers don't seem to be complaining.

One could always do something like this:
Code:
...
int exit_code;

...
exit_code = 0;
for (...) {
   if (exit_code)
      break;
   ...
   for (...) {
      if (exit_code)
         break;
      ...
      if (exit_code)
         break;
      ...
   }
   if (exit_code)
      break;
   ...
}
...
if (exit_code) {
   ...
}
else {
   ...
}
...
return (...);

This is easy to extend this for deeper nesting levels, but if you happen to have deeper nesting, I suspect you need to break your routine into smaller pieces.

tepples wrote:
...it's possible to implement, say, exceptions and coroutines in the C language rather cleanly in terms of macros on top of goto.


C has poor support for coroutines. I think that a switch statement is the way to go, goto strikes me as a broken solution. See this page.

I'm of the opinion that gotos should always jump forward. I'd be willing to use a goto if it ever seemed like it would help. I have yet to program anything that would truly benefit from the use of a goto.

Using a goto will defeat the compiler's ability to optimize. Attempting to use a goto to save speed is misguided-
A) Premature optimization is the root of all evil
B) You want to recode the routine in assembler

-Brendan

#19220 - jma - Wed Apr 14, 2004 5:53 am

tepples wrote:
jma wrote:
Break can be used in for loops, switch/case statements and do/while loops.

True, but unlike the break in the Java language which can jump from deep nesting within a block to the end of that block, ..., the break in C and C++ can't jump out of more than one such block at a time.

So? I don't know (or care to really know much) about Java. However, if you need to break out of more than one loop, you need to factor your code some more and rethink your implementation.

[more stuff snipped from other posts that were all good comments]

The fact is, you never want to use a goto to leave any kind of loop for speed reasons. I say this, because this was the original argument made for using it (not because it is the only or most important reason). All the reasons stated by everyone are true, plus other reasons.

If you need to do it, fine. Does it make something easier at the moment? fine -- it isn't my code, why should I care? :)

However, it will give your compiler a lot of grief! And on an embedded system, you want to help the compiler as much as you can. Compilers (especially GCC) are hardly great at code generation. Even the best only produce code that rivals that of intermediate assembly programmers. THe real advantage of compilers is in cycle counting and knowledge of branch prediction and other processor specifics.

The ARM happens to be a very nice processor (lots of available registers), but most processors aren't so generous. And programming practices developed now stick with you in the future.

For loops (as this is the primary example being given) do not have a standard implementation -- only a standard effect. The loop variables may be in registers -- they may not. Those registers may needed to be saved upon entering, and restored upon leaving, the loop:

Code:
for(i = 0;i < 10;i++)
    if (i == 7) goto oops;
/* do something here */
oops:
/* do something else */


The C compiler may have a lot of cleanup code after the loop, but before 'oops'. Your goto may not just compile to a simple branch statement, but rather a lot of clean-up code on top of it.

Now, devil's advocate: the C standard has goto. It's there because it is legacy, and because it is useful still. If it wasn't useful, what would be the point. 99% of the time, if you are going to use goto, rethink your code, you are probably just trying to avoid typing a code segment twice. If that's the case, bust out a subroutine -- ie factor!

If you are using a goto to bust out of multiple loops, again, rethink the code. Instead of:
Code:
for (...) {
    for (...) {
        if (...) goto bust_loop;
    }
}
bust_loop:

do:
Code:
void my_factored_code() {
    for(...) {
        for(...) {
            if (...) return;
        }
    }
}


Oh well, this debate has been argued ad nauseum on c.l.c and probably here numerous times as well. This is basic, freshman level stuff, people (well, the compiler theory is a bit higher). But in the immortal words of Forrest Gump, "that's all I have to say about that." :)

Have fun coding... and above all, use the right tool for the right problem. If all you have is a hammer, everything looks like a nail. Remember, C has a lot of tools, use them all, but use them wisely. :)

Jeff
_________________
massung@gmail.com
http://www.retrobyte.org

#19235 - dagamer34 - Wed Apr 14, 2004 11:37 pm

One thing I want to add. Goto shouldn't need to be used in a function; that means that it's trying to do too much.
_________________
Little kids and Playstation 2's don't mix. :(