Examples of good gotos in C or C++ [closed]
Asked Answered
B

16

79

In this thread, we look at examples of good uses of goto in C or C++. It's inspired by an answer which people voted up because they thought I was joking.

Summary (label changed from original to make intent even clearer):

infinite_loop:

    // code goes here

goto infinite_loop;

Why it's better than the alternatives:

  • It's specific. goto is the language construct which causes an unconditional branch. Alternatives depend on using structures supporting conditional branches, with a degenerate always-true condition.
  • The label documents the intent without extra comments.
  • The reader doesn't have to scan the intervening code for early breaks (although it's still possible for an unprincipled hacker to simulate continue with an early goto).

Rules:

  • Pretend that the gotophobes didn't win. It's understood that the above can't be used in real code because it goes against established idiom.
  • Assume that we have all heard of 'Goto considered harmful' and know that goto can be used to write spaghetti code.
  • If you disagree with an example, criticize it on technical merit alone ('Because people don't like goto' is not a technical reason).

Let's see if we can talk about this like grown ups.

Edit

This question seems finished now. It generated some high quality answers. Thanks to everyone, especially those who took my little loop example seriously. Most skeptics were concerned by the lack of block scope. As @quinmars pointed out in a comment, you can always put braces around the loop body. I note in passing that for(;;) and while(true) don't give you the braces for free either (and omitting them can cause vexing bugs). Anyway, I won't waste any more of your brain power on this trifle - I can live with the harmless and idiomatic for(;;) and while(true) (just as well if I want to keep my job).

Considering the other responses, I see that many people view goto as something you always have to rewrite in another way. Of course you can avoid a goto by introducing a loop, an extra flag, a stack of nested ifs, or whatever, but why not consider whether goto is perhaps the best tool for the job? Put another way, how much ugliness are people prepared to endure to avoid using a built-in language feature for its intended purpose? My take is that even adding a flag is too high a price to pay. I like my variables to represent things in the problem or solution domains. 'Solely to avoid a goto' doesn't cut it.

I'll accept the first answer which gave the C pattern for branching to a cleanup block. IMO, this makes the strongest case for a goto of all the posted answers, certainly if you measure it by the contortions a hater has to go through to avoid it.

Besnard answered 29/10, 2008 at 3:58 Comment(8)
I don't get why the gotophobes don't just mandate "#define goto report_to_your_supervisor_for_re_education_through_labour" at the top of the project's include file. If it's always wrong, make it impossible. Otherwise, it's sometimes right...Trajectory
"can't be used in real code"? I use it in "real" code any time it's the best tool for the job. "Established idiom" is a nice euphemism for "blind dogmatism".Dzungaria
I agree that "goto" can be useful (there are great examples below), but I disgagree with your specific example. A line that says "goto infinite_loop" sounds like it means "go to the part of the code where we're going to start looping forever," as in "initialize(); set_things_up(); goto infinite_loop;" when what you really mean to convey is "begin the next iteration of the loop we're already in," which is completely different. If you're trying to loop, and your language has constructs designed specifically for looping, use those for clarity. while(true) {foo()} is pretty unambiguous.Pathogenic
One major downside to your example is that it is not apparent which ohter places in the code may decide to jump to this label. A "normal" infinite loop (for (;;)) has no surprising entry points.Atcliffe
@György: yes, but that is not an entry point.Atcliffe
See Knuth's paper "Structured Programming with go to Statements".Jordan
#7335452Daryn
Another way if someone want to kill you because goto, try to use do{}while(0); like in this construction do{ if( 1 > 0 ){ break;} } while(0);Destine
V
87

Heres one trick I've heard of people using. I've never seen it in the wild though. And it only applies to C because C++ has RAII to do this more idiomatically.

void foo()
{
    if (!doA())
        goto exit;
    if (!doB())
        goto cleanupA;
    if (!doC())
        goto cleanupB;

    /* everything has succeeded */
    return;

cleanupB:
    undoB();
cleanupA:
    undoA();
exit:
    return;
}
Vacla answered 29/10, 2008 at 4:8 Comment(11)
What's wrong with this? (apart from the fact that comments don't understand newlines) void foo() { if (doA()) { if (doB()) { if (doC()) { /* everything succeeded */ return; } undoB(); } undoA(); } return; }Antisocial
The extra blocks cause unnecessary indentation that's hard to read. It also doesn't work if one of the conditions is inside a loop.Mahogany
You can see a lot of this kind of code in most low level Unix things (like the linux kernel, for example). In C, that's the best idiom for error recovering IMHO.Bahia
As cournape mentioned, the Linux kernel uses this style all the time.Reinaldo
Not only linux kernel take a look in Windows driver samples from microsoft and you will find same pattern. In general this is a C way to handle exceptions and very useful one :). I usually prefer only 1 label and in few cases 2. 3 can be avoided in 99% of cases.Snooperscope
I've got in trouble at work so many times for using this 'pattern', but I still think it can be the clearest approach to some situations...Homage
I've also got in trouble for using multiple returns (also usefull in some situations to prevent endless indent levels and indecipherable tortuous code paths).Homage
Why is that better than just calling undoB() directly?Horvath
@Horvath Old thread I know, but it is because then you would have to do undoB(); undoA(); /*goto exit; or return;*/ and if you had a doD() you would have to undoC() as well as all the items undone in a failed doC().Alar
That is exactly how goto works in an MS-DOS bat file.Jaleesa
I agree with Greg, C++ has RAII, so you will never need such code, but for C code , I would favor @Antisocial code without goto.Apollus
K
85

The classic need for GOTO in C is as follows

for ...
  for ...
    if(breakout_condition) 
      goto final;

final:

There is no straightforward way to break out of nested loops without a goto.

Kunstlied answered 29/10, 2008 at 4:31 Comment(13)
To add to this: the way to do it without a goto would be to set a boolean flag, and test that flag each iteration of each outer loop. This produces less readable code; it's also marginally less efficient, but that's only relevant in a rare number of cases (e.g. kernel code).Mahogany
Most often when this need comes up I can use a return instead. But you need to be writing small functions for it to work out that way.Dumanian
I definitely agree with Darius - refactor it to a function and return instead!Sylph
Johnathan - break only breaks out of the closest loop. Darius - sure. But this is the classic Goto In C example.Kunstlied
@metao: Bjarne Stroustrup disagree. On his C++ Programming Language book, this is exactly the example given of a "good use" of goto.Isotherm
@Adam Rosenfield: The whole problem with goto, highlighted by Dijkstra, is that structured programming offers more comprehensible constructs. Making code harder to read in order to avoid goto proves that the author failed to understand that essay...Trajectory
Why would you have final in that code? Break it out into a function.Intermittent
@Steve Jessop: Not only have people misunderstood the essay, most of the "no go to" cultists don't understand the historical context in which it was written.Tann
This case is very rare and is the only one case I actually use Goto, especially if the function is complicated and Call Stack must not be reframed! The other one is at https://mcmap.net/q/24148/-to-goto-or-not-to-goto-closedCollimore
@DariusBacon Actually, return doesn't always do a treat, because if you for example have allocated memory, or other resources, you probably want to free them before returning, that's where goto is awesome.Katelyn
@Katelyn In fact return is enough. if you are worried about freeing memory taking runtime, use inline function. if you want to preserve value, use pointersApollus
@MuhammadAnnaqeeb modern compilers ignore inline and have their own mind about inlining functions, so there's no warranty. Lets say you allocate A, do something, then B, do something, then C, so on... and at any point you may fail and you need to free only current and previous, e.g. if you fail at B, you free B and A, but not C and up. In this scenario, without goto, you will get a ton of code from inlining or a ton of function calls.Katelyn
I'm 95% certain that a return statement will generate a jump to the end of the function to return (assuming you have a stack frame to collapse), so it's not really any different than using a goto except for source readability. But, like it's been pointed out, goto is a four-letter word... ;)Meuse
B
32

Here's my non-silly example, (from Stevens APITUE) for Unix system calls which may be interrupted by a signal.

restart:
    if (system_call() == -1) {
        if (errno == EINTR) goto restart;

        // handle real errors
    }

The alternative is a degenerate loop. This version reads like English "if the system call was interrupted by a signal, restart it".

Besnard answered 29/10, 2008 at 4:7 Comment(9)
this way is used for example linux scheduler have a goto like this but i would say there are very few cases where backwards goto's are acceptable and in general should be avoided.Snooperscope
while(1){if(system_call() == -1){if(errno == EINTR)continue;//handle real errors}}Wanderjahr
@Amarghosh, continue is just a goto in a mask.Monet
@Monet and is more safe/usable (without leading to spaghetti code) than gotoWanderjahr
@Monet ...hmm.. for the sake of argument, yeah; you can make good readable code with goto and spaghetti code with continue.. Ultimately it depends on the person who writes the code. The point is it is easy to get lost with goto than with continue. And newbies normally use the first hammer they get for every problems.Wanderjahr
@Amarghosh. Your 'improved' code isn't even equivalent to the original - it loops forever in the success case.Besnard
@Besnard oopsie.. it's gonna need two else breaks (one for each ifs) to make it equivalent.. what can I say... goto or not, your code is only as good as you :(Wanderjahr
@Wanderjahr no need to "goto" nor "else" . while(system_call() == -1) if (errno == EINTR) continue; /* handle real errors*/ break; }Apollus
There is already TEMP_FAILURE_RETRY macro for this (defined in unistd.h). It is implemented without using goto: while (__result == -1L && errno == EINTR);.Eucharis
E
14

If Duff's device doesn't need a goto, then neither should you! ;)

void dsend(int count) {
    int n;
    if (!count) return;
    n = (count + 7) / 8;
    switch (count % 8) {
      case 0: do { puts("case 0");
      case 7:      puts("case 7");
      case 6:      puts("case 6");
      case 5:      puts("case 5");
      case 4:      puts("case 4");
      case 3:      puts("case 3");
      case 2:      puts("case 2");
      case 1:      puts("case 1");
                 } while (--n > 0);
    }
}

code above from Wikipedia entry.

Economize answered 29/10, 2008 at 4:8 Comment(5)
Cmon guys, if you're going to downvote, a short comment as to why would be great. :)Economize
This is an example of a logical fallacy also known as the "appeal to authority". Check it out on Wikipedia.Selfacting
Don't put this device in your code though -- on modern machines it's usually slower than memcpy()Ilario
Duff's Device is the proof why goto isn't harmful: it doesn't use goto and nevertheless is as spaghetti-like as mankind only rarely sees. And if this is the only way to prevent a goto, I'd prefer the latter. So +0,5 for mentioning Duff's Device, but -1,1 for using it for arguing against goto.Swallowtailed
@glglgl: A switch statement is C's version of HP Basic's GOTO I OF xx,yy,zz statement. The targets are preceded by the word case, but and are scope-limited, but switch is very much a goto-based structure, as evidenced by its fall-through rules.Barncard
K
14

Knuth has written a paper "Structured programming with GOTO statements", you can get it e.g. from here. You'll find many examples there.

Karlsruhe answered 29/10, 2008 at 7:17 Comment(2)
That paper is so out of date, it's not even funny. Knuth's assumptions there simply don't hold any longer.Triatomic
Which assumptions? His examples are as real today for a procedural language as C (he gives them in some pseudo-code) as they were at that time.Karlsruhe
T
12

I have nothing against gotos in general, but I can think of several reasons why you wouldn't want to use them for a loop like you mentioned:

  • It does not limit scope hence any temp variables you use inside won't be freed until later.
  • It does not limit scope hence it could lead to bugs.
  • It does not limit scope hence you cannot re-use the same variable names later in future code in the same scope.
  • It does not limit scope hence you have the chance of skipping over a variable declaration.
  • People are not accustomed to it and it will make your code harder to read.
  • Nested loops of this type can lead to spaghetti code, normals loops will not lead to spaghetti code.
Tomkins answered 29/10, 2008 at 4:1 Comment(3)
is inf_loop: {/* loop body */} goto inf_loop; better? :)Akee
Points 1-4 are dubious for this example even without the braces. 1 It's an infinite loop. 2 Too vague to address. 3 Trying to hide a variable of the same name in an enclosing scope is poor practice. 4. A backward goto can't skip a declaration.Besnard
1-4 as with all infinite loops, you usually have some kind of break condition in the middle. So they are applicable. Re a bakward goto can't skip a declaration... not all gotos are backwards...Tomkins
H
12

Very common.

do_stuff(thingy) {
    lock(thingy);

    foo;
    if (foo failed) {
        status = -EFOO;
        goto OUT;
    }

    bar;
    if (bar failed) {
        status = -EBAR;
        goto OUT;
    }

    do_stuff_to(thingy);

OUT:
    unlock(thingy);
    return status;
}

The only case I ever use goto is for jumping forwards, usually out of blocks, and never into blocks. This avoids abuse of do{}while(0) and other constructs which increase nesting, while still maintaining readable, structured code.

Houseboy answered 29/10, 2008 at 4:18 Comment(4)
I think this is the typcial C way of error handling. I can not see it replaced nicely, better readable any other way RegardsCinema
Why not... do_stuff(thingy) { RAIILockerObject(thingy); ..... /* -->*/ } /* <---*/ :)Collimore
@ПетърПетров That's a pattern in C++ that has no analogue in C.Houseboy
Or in an even more structured language than C++, try { lock();..code.. } finally { unlock(); } But yeah C has no equivalent.Prader
B
7

One good place to use a goto is in a procedure that can abort at several points, each of which requires various levels of cleanup. Gotophobes can always replace the gotos with structured code and a series of tests, but I think this is more straightforward because it eliminates excessive indentation:

if (!openDataFile())
  goto quit;

if (!getDataFromFile())
  goto closeFileAndQuit;

if (!allocateSomeResources)
  goto freeResourcesAndQuit;

// Do more work here....

freeResourcesAndQuit:
   // free resources
closeFileAndQuit:
   // close file
quit:
   // quit!
Burgundy answered 29/10, 2008 at 4:9 Comment(2)
I would replace this with a set of functions: freeResourcesCloseFileQuit, closeFileQuit, and quit.Lemieux
If you created these 3 extra functions, you'd need to pass pointers/handles to the resources and files to be freed/closed. You'd probably make freeResourcesCloseFileQuit() call closeFileQuit(), which in turn would call quit(). Now you have 4 tightly coupled functions to maintain, and 3 of them will probably be called at most once: from the single function above. If you insist on avoiding goto, IMO, nested if() blocks have less overhead and are easier to read and maintain. What do you gain with 3 extra functions?Burgundy
E
7

@fizzer.myopenid.com: your posted code snippet is equivalent to the following:

    while (system_call() == -1)
    {
        if (errno != EINTR)
        {
            // handle real errors

            break;
        }
    }

I definitely prefer this form.

Economize answered 29/10, 2008 at 4:15 Comment(9)
OK, I know the break statement is just a more acceptable form of goto..Economize
To my eyes, it's confusing. It's a loop which you don't expect to enter in the normal execution path, so the logic seems backwards. Matter of opinion, though.Besnard
Backwards? It starts, it continues, it falls through. I think this form more obviously states its intentions.Economize
I'd agree with fizzer here that the goto provides a clearer expectation as to the condition's value.Selfacting
OK we will have to disagree on that one.Economize
+1 because the while version is both clearer and avoid the use of goto, which have some limitation of passing through object construction.Isotherm
In which way is this snippet easier to read than fizzer's.Lohengrin
I just ran into something similar in my code and I couldn't use this solution because the break was inside of another loop. So the goto solution definitely has use cases where it's clearer/simpler.Molder
paercebal uses the old argument that avoiding goto is better because it avoids goto... I assume he doesn't quite understand the meaning of errno == EINTR. The while version is not clearer at all. It gives you the completely wrong impression that we have a waiting loop here that needs to be iterated until it succeeds. The goto catches the reader's attention and makes him or her actually understand the code.Sela
V
6

Even though I've grown to hate this pattern over time, it's in-grained into COM programming.

#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
  HRESULT hr = S_OK;
  IfFailGo( pFoo->PerformAction() );
  IfFailGo( pFoo->SomeOtherAction() );
Error:
  return hr;
}
Ventilate answered 29/10, 2008 at 5:7 Comment(0)
S
5

Here is an example of a good goto:

// No Code
Saurian answered 29/10, 2008 at 4:1 Comment(2)
This isn't really helpfulSouthbound
@Saurian FYI: Found this in the low quality review queue. Consider editing.Springtime
B
1

I've seen goto used correctly but the situations are normaly ugly. It is only when the use of goto itself is so much less worse than the original. @Johnathon Holland the poblem is you're version is less clear. people seem to be scared of local variables:

void foo()
{
    bool doAsuccess = doA();
    bool doBsuccess = doAsuccess && doB();
    bool doCsuccess = doBsuccess && doC();

    if (!doCsuccess)
    {
        if (doBsuccess)
            undoB();
        if (doAsuccess)
            undoA();
    }
}

And I prefer loops like this but some people prefer while(true).

for (;;)
{
    //code goes here
}
Bunion answered 29/10, 2008 at 11:9 Comment(2)
Never, ever, ever compare a boolean value to true or false. It's already a boolean; just use "doBsuccess = doAsuccess && doB();" and "if (!doCsuccess){}" instead. It's simpler and protects you from clever programmers who write things like "#define true 0" because, well, because they can. It also protects you from programmers who mix int's and bool's, and then assume any non-zero value is true.Burgundy
Thank you and point taken == true removed. This is closer to my real style anyway. :)Bunion
F
0

My gripe about this is that you lose block scoping; any local variables declared between the gotos remains in force if the loop is ever broken out of. (Maybe you're assuming the loop runs forever; I don't think that's what the original question writer was asking, though.)

The problem of scoping is more of an issue with C++, as some objects may be depending on their dtor being called at appropriate times.

For me, the best reason to use goto is during a multi-step initialization process where the it's vital that all inits are backed out of if one fails, a la:

if(!foo_init())
  goto bye;

if(!bar_init())
  goto foo_bye;

if(!xyzzy_init())
  goto bar_bye;

return TRUE;

bar_bye:
 bar_terminate();

foo_bye:
  foo_terminate();

bye:
  return FALSE;
Fidelia answered 29/10, 2008 at 4:11 Comment(0)
B
0

I don't use goto's myself, however I did work with a person once that would use them in specific cases. If I remember correctly, his rationale was around performance issues - he also had specific rules for how. Always in the same function, and the label was always BELOW the goto statement.

Beadle answered 29/10, 2008 at 5:53 Comment(1)
Additional data: note that you cannot use goto to go outside the function, and that in C++, it is forbidden to bypass an object construction through a gotoIsotherm
H
0
#include <stdio.h>
#include <string.h>

int main()
{
    char name[64];
    char url[80]; /*The final url name with http://www..com*/
    char *pName;
    int x;

    pName = name;

    INPUT:
    printf("\nWrite the name of a web page (Without www, http, .com) ");
    gets(name);

    for(x=0;x<=(strlen(name));x++)
        if(*(pName+0) == '\0' || *(pName+x) == ' ')
        {
            printf("Name blank or with spaces!");
            getch();
            system("cls");
            goto INPUT;
        }

    strcpy(url,"http://www.");
    strcat(url,name);
    strcat(url,".com");

    printf("%s",url);
    return(0);
}
Hefter answered 9/1, 2014 at 21:58 Comment(0)
S
-1

@Greg:

Why not do your example like this:

void foo()
{
    if (doA())
    {    
        if (doB())
        {
          if (!doC())
          {
             UndoA();
             UndoB();
          }
        }
        else
        {
          UndoA();
        }
    }
    return;
}
Saurian answered 29/10, 2008 at 4:15 Comment(12)
It adds unnecessary levels of indentation, which can get really hairy if you have a large number of possible failure modes.Mahogany
I'd take indentations over goto anyday.Saurian
Also, it's far more lines of code, has a redundant function call in one of the cases and is far harder to add a doD to correctly.Orgy
I agree with Patrick - I've seen this idiom used with several conditions that nested to a depth of about 8 levels. Very nasty. And very error prone, especially when trying to add something new to the mix.Flanker
This code is just scream "I will have a bug in few next weeks" somebody will forget to undo something. You need all your undo in same place. It's just like "finally" in native OO languages.Snooperscope
I don't like the copy-and-pasted repetition of "UndoA()". C'n'P code is approximately as bad as goto, and for me in this case it loses because it's more verbose. Also the fact that B is done after A, and also undone after A, makes my teeth curl, but that's not part of the pattern here.Trajectory
You could rewrite this with less nesting by doing if(!doA()) { return; } if(!doB()) { UndoA(); return; } and so on. You'd still have the duplicate function calls, but at least the code would be readable.Rigsby
Why couldn't you use a case statement here?Intermittent
@AlexandreC. And if you work under C, RAII falls away and you have goto >> this crap.Swallowtailed
@Intermittent That would be feasible as well, under certain circumstances. The rule should be "Avoid goto if there are feasible alternatives, but use it when appropriate."Swallowtailed
And today I found a great illustration what pyramid code looks like. msdn.microsoft.com/en-us/library/windows/desktop/…Triggerhappy
I assume the question was meant in a sarcastic way, because the code very clearly shows the usefulness of goto, or at least demonstrates that you can write code that is worse than using goto.Sela

© 2022 - 2024 — McMap. All rights reserved.