Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I really hesitate to ask this, because I don't want to "solicit debate, arguments, polling, or extended discussion" but I'm new to C and want to gain more insight into common patterns used in the language.

I recently heard some distaste for the goto command, but I've also recently found a decent use-case for it.

Code like this:

error = function_that_could_fail_1();
if (!error) {
    error = function_that_could_fail_2();
    if (!error) {
        error = function_that_could_fail_3();
        ...to the n-th tab level!
    } else {
        // deal with error, clean up, and return error code
    }
} else {
    // deal with error, clean up, and return error code
}

If the clean-up part is all very similar, could be written a little prettier (my opinion?) like this:

error = function_that_could_fail_1();
if(error) {
    goto cleanup;
}
error = function_that_could_fail_2();
if(error) {
    goto cleanup;
}
error = function_that_could_fail_3();
if(error) {
    goto cleanup;
}
...
cleanup:
// deal with error if it exists, clean up
// return error code

Is this a common or acceptable use-case of goto in C? Is there a different/better way to do this?

share|improve this question
2  
See also this question (and my answer). –  Keith Thompson Jun 30 '12 at 3:32
1  
@DeadMG You'd still end up with lots of nesting. –  Izkata Jun 30 '12 at 3:40
1  
@Izkata: No, the point is to have functions (like destructors) which are implicitly called by the compiler when an exception is thrown. –  DeadMG Jun 30 '12 at 8:08
3  
@DeadMG: Using another language is not always an option. –  Benjamin Kloster Jun 30 '12 at 11:38
8  
I think with appropriate label naming, example may look quite decent: goto hell; –  gnat Jun 30 '12 at 11:50

11 Answers 11

up vote 22 down vote accepted

The goto statement (and its corresponding labels) are a flow control primitive (along with conditional execution of a statement). By that, I mean that they are there to allow you to construct program flow control networks. You can think of them as modeling the arrows between the nodes of a flowchart.

Some of these can be optimized out immediately, where there is a direct linear flow (you just use a sequence of basic statements). Other patterns are best replaced with structured programming constructs where these are available; if it looks like a while loop, use a while loop, OK? The structured programming patterns are definitely at least potentially clearer of intent than a mess of goto statements.

Yet C does not include all possible structured programming constructs. (It's not clear to me that all relevant ones have been discovered yet; the rate of discovery is slow now, but I'd hesitate to jump to saying that all have been found.) Of the ones we know about, C definitely lacks the try/catch/finally structure (and exceptions too). It also lacks multi-level break-from-loop. These are the kinds of things which a goto can be used to implement. It's possible to use other schemes to do these too — we do know that C has a sufficient set of non-goto primitives — but these often involve creating flag variables and much more complex loop or guard conditions; increasing the entanglement of the control analysis with the data analysis makes the program harder to understand overall. It also makes it more difficult for the compiler to optimize and for the CPU to execute rapidly (most flow control constructs — and definitely goto — are very cheap).

Thus, while you shouldn't use goto unless needed, you should be aware that it exists and that it may be needed, and if you need it, you shouldn't feel too bad. An example of a case where it is needed is resource deallocation when a called function returns an error condition. (That is, try/finally.) It's possible to write that without goto but doing that can have downsides of its own, such as the problems of maintaining it. An example of the case:

int frobnicateTheThings() {
    char *workingBuffer = malloc(...);
    int i;

    for (i=0 ; i<numberOfThings ; i++) {
        if (giveMeThing(i, workingBuffer) != OK)
            goto error;
        if (processThing(workingBuffer) != OK)
            goto error;
        if (dispatchThing(i, workingBuffer) != OK)
            goto error;
    }

    free(workingBuffer);
    return OK;

  error:
    free(workingBuffer);
    return OOPS;
}

The code could be even shorter, but it's enough to demonstrate the point.

share|improve this answer
    
+1: In C goto is technically never "needed" - there is always a way to do it, it gets messy..... For a robust set of guidelines for use of goto look at MISRA C. –  mattnz Oct 24 '12 at 21:08

Yes.

It is used in, for example, the linux kernel. Here's an email from the end of a thread from nearly a decade ago, bolding mine:

From: Robert Love
Subject: Re: any chance of 2.6.0-test*?
Date: 12 Jan 2003 17:58:06 -0500

On Sun, 2003-01-12 at 17:22, Rob Wilkens wrote:

I say "please don't use goto" and instead have a "cleanup_lock" function and add that before all the return statements.. It should not be a burden. Yes, it's asking the developer to work a little harder, but the end result is better code.

No, it is gross and it bloats the kernel. It inlines a bunch of junk for N error paths, as opposed to having the exit code once at the end. Cache footprint is key and you just killed it.

Nor is it easier to read.

As a final argument, it does not let us cleanly do the usual stack-esque wind and unwind, i.e.

        do A
        if (error)
            goto out_a;
        do B
        if (error)
            goto out_b;
        do C
        if (error)
            goto out_c;
        goto out;
        out_c:
        undo C
        out_b:
        undo B:
        out_a:
        undo A
        out:
        return ret;

Now stop this.

Robert Love

That said, it requires a lot of discipline to keep yourself from creating spaghetti code once you get used to using goto, so unless you're writing something that requires speed and a low memory footprint (like a kernel or embedded system) you should really think about it before you write the first goto.

share|improve this answer
10  
Note that a kernel is different from a non-kernel program regarding the priority on raw speed versus readability. In other words, they have ALREADY profiled and found that they need to optimize their code for speed with goto. –  user1249 Jul 2 '12 at 16:13
    
The link seems to be dead (which is a shame since I would like to read the full discussion). Good answer nonetheless. –  Leo Oct 22 '12 at 6:42
    
@Leo: Ok. Try this link instead: lkml.org/lkml/2003/1/12/203 –  Kevin Cathcart Oct 24 '12 at 18:43
    
@KevinCathcart Thanks for looking it up, I've replaced the link in the answer –  Izkata Oct 24 '12 at 19:58
1  
Using stack un-wind to handle clean up on error without actually pushing onto the stack! This is an awesome use of goto. –  mike30 Oct 24 '12 at 20:57

A famous paper describing case of valid use of was Structured Programming with GOTO Statement by Donald E. Knuth (Stanford University). The paper appeared in the days where using GOTO was considered a sin by some and when the movement for Structured Programming was the at its peak. You may want to take a look at GoTo Considered Harmful.

share|improve this answer
for (int y=0; y<height; ++y) {
    for (int x=0; x<width; ++x) {
        if (find(x, y)) goto found;
    }
}
found:
share|improve this answer
    
If there's only one loop, break works exactly like goto, though bearing no stigma. –  9000 Jun 30 '12 at 16:20
4  
-1: First, x and y are OUT OF SCOPE at found:, so this doesn't help you any. Second, with the code as written, the fact that you arrived at found: does not mean you found what you were looking for. –  John R. Strohm Jul 1 '12 at 22:43
    
It's because this is the smallest example I could think of for the case of breaking out of a multiple number of loops. Please feel free to edit it for a better label or a done check. –  aib Jul 2 '12 at 15:12
    
But also bear in mind that C functions are not necessarily side-effect-free. –  aib Jul 2 '12 at 15:12

I also use goto if the alternative do/while/continue/break hackery would be less readable.

gotos have the advantage that their targets have a name and they read goto something;. This may be more readable than break or continue if you're not actually stopping or continuing something.

share|improve this answer
    
Could you give an example where break or continue do something other than stopping or continuing a loop? –  Benjamin Kloster Jun 30 '12 at 11:41
3  
Anywhere inside a do ... while(0) or another construct which isn't an actual loop but a harebrained attempt to prevent the usage of a goto. –  aib Jun 30 '12 at 12:39
    
Ah, thanks, I didn't know this particular brand of "Why the hell would someone do that?!" constructs yet. –  Benjamin Kloster Jun 30 '12 at 13:09
    
@BenjaminKloster: break is also used to mark the end of cases in a switch statement. –  dan04 Jun 30 '12 at 23:43
2  
Usually, the do/while/continue/break hackery only becomes unreadable when the module containing it is way too fricking long in the first place. –  John R. Strohm Jul 1 '12 at 22:44

I think it is a decent use case, but in case "error" is nothing more than a boolean value, there is a different way to accomplish what you want:

error = function_that_could_fail_1();
error = error || function_that_could_fail_2();
error = error || function_that_could_fail_3();
if(error)
{
     // do cleanup
}

This makes use of the short-circuit evaluation of boolean operators. If this "better", is up to your personal taste and how you are accustomed to that idiom.

share|improve this answer
1  
The problem with this is that error's value could become meaningless with all the OR'ing. –  James Jun 30 '12 at 15:47
    
@James: edited my answer due to your comment –  Doc Brown Jul 1 '12 at 17:17
    
This is not sufficient. If an error occurred during the first function, I do not want to execute the second or third function. –  Robz Jul 2 '12 at 22:41
    
If with short-hand evaluation you mean short-circuit evaluation, this is exactly not what's done here due to the use of bitwise OR instead of logical OR. –  Christian Rau Jul 3 '12 at 8:00
    
@ChristianRau: thanks, edited my answer accordingly –  Doc Brown Jul 3 '12 at 13:08

In Java you'd do it like this:

makeCalls:  {
    error = function_that_could_fail_1();
    if (error) {
        break makeCalls;
    }
    error = function_that_could_fail_2();
    if (error) {
        break makeCalls;
    }
    error = function_that_could_fail_3();
    if (error) {
        break makeCalls;
    }
    ...
    return 0;  // No error code.
}
// deal with error if it exists, clean up
// return error code

I use this a lot. Much as I dislike goto's, in most other C-style languages I use your code; there's no other good way to do it. (Jumping out of nested loops is a similar case; in Java I use a labeled break and everywhere else I use a goto.)

share|improve this answer
    
Aw that's a neat control structure. –  insta Jul 2 '12 at 16:10
    
This also works in Javascript. I guess there is one thing where it is similar to Java after all. –  hugomg Jul 2 '12 at 21:42
3  
This is really interesting. I would normally think of using try/catch/finally structure for this in java (throwing exceptions instead of breaking). –  Robz Jul 2 '12 at 22:55
    
@Robz: if the methods are as regular as in this example, and you can make them throw properly, try/catch/finally could actually work here and be beautiful. (Normally I find them awkward and ugly, even when they are the best solution.) But this kind of break works even when the conditions are complex, involved, and contrary. It works when nothing else will, and looks good doing it. –  RalphChapin Jul 3 '12 at 2:14
    
That is really unreadable (at least for me). If present, exceptions are far better. –  m3th0dman Jul 4 '12 at 9:33

Personally I'd refactor it more like this:

int DoLotsOfStuffThatCouldFail (paramstruct *params)
{
    int errcode = EC_NOERROR;

    if ((errcode = FunctionThatCouldFail1 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail2 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail3 (params)) != EC_NOERROR) return errcode;
    if ((errcode = FunctionThatCouldFail4 (params)) != EC_NOERROR) return errcode;

    return EC_NOERROR;
}

void DoStuff (paramstruct *params)
{
    int errcode = EC_NOERROR;

    InitStuffThatMayNeedToBeCleaned (params);

    if ((errcode = DoLotsOfStuffThatCouldFail (params)) != EC_NOERROR)
    {
         CleanupAfterError (params, errcode);
    }
}

That would be more motivated by avoiding the deep nesting than avoiding the goto however (IMO a worse problem with the first code sample), and would of course be dependent on CleanupAfterError being possible out-of-scope (in this case "params" could be a struct containing some allocated memory that you need to free, a FILE * that you need to close or whatever).

One major advantage I see with this approach is that it's both easier and cleaner to slot a hypothetical future extra step between, say, FTCF2 and FTCF3 (or remove an existing current step), so it lends itself better to maintainability (and the person who inherits my code not wanting to lynch me!) - goto aside, the nested version lacks that.

share|improve this answer
1  
I didn't state this in my question, but it is possible that the FTCFs do NOT have the same parameters, making this pattern a little more complicated. Thanks though. –  Robz Jul 2 '12 at 22:47

There will always be camps that say one way is acceptable and another that is not. Companies I've worked for have frowned or strongly discouraged goto use. Personally, I can't think of any time I've used one, but that doesn't mean they are bad, just another way of doing things.

In C, I typically do the following:

  • Test for conditions that could prevent processing (bad inputs, etc) and "return"
  • Do all steps that require resource allocation (e.g. mallocs)
  • Perform the processing, where multiple steps check for success
  • Release any resources, if successfully allocated
  • Return any results

For the processing, using your goto example, I'd do this:

error = function_that_could_fail_1(); if(!error) { error = function_that_could_fail_2(); } if(!error) { error = function_that_could_fail_3(); }

There no nesting, and inside the if clauses, you can do any error reporting, if the step generated an error. So, it doesn't have to be "worse" than a method using gotos.

I have yet to run across a case where someone has gotos that can't be done with another method and is just as readable/understandable and that is the key, IMHO.

share|improve this answer

Take a look at the MISRA (Motor Industry Software Reliability Association) C coding guidelines that allow goto under strict criteria (Which your example meets)

Where I work the same code would be written - no goto needed - Avoiding needless religious debate about them is a big plus in any software house.

error = function_that_could_fail_1();
if(!error) {
  error = function_that_could_fail_2();
}
if(!error) {
  error = function_that_could_fail_3();
} 
if(!error) {
...
if (error) {
  cleanup:
} 

or for "goto in drag" - something even more dodgy than goto, but gets around the "No goto Ever!!!" camp) "Surely it must be OK, does not use Goto" ....

do {
  if (error = function_that_could_fail_1() ){
    break 
  }
  if (error = function_that_could_fail_2() ){
    break 
  }
  ....... 
} while (0) 
cleanup();
.... 

If the functions have the same parameter type, put them into a table and use a loop -

share|improve this answer
1  
The current MISRA-C:2004 guidelines do not allow goto in any form (see rule 14.4). Note the MISRA committee has always been confused about this, they don't know which foot to stand on. First, they unconditionally banned the use of goto, continue etc. But in the draft for the upcoming MISRA 2011 they want to allow them again. As a sidenote, please note that MISRA bans assignment inside if-statements, for very good reasons since it is far more dangerous than any use of goto. –  user29079 Jul 4 '12 at 9:25
    
From an analytical perspective, adding a flag to a program is equivalent to duplicating all the code code where the flag is in scope, having every if(flag) in one copy take the "if" branch, and having each corresponding statements in the other copy take the "else". Actions which set and clear the flag are really "gotos" that jump between those two versions of the code. There are times when use of flags is cleaner than any alternative, but adding a flag to save one goto target is not a good trade-off. –  supercat Mar 27 at 18:50

In my opinion, the code you posted is an example of a valid use of goto, because you only jump downwards and only use like a primitive exception handler.

However, because of the old goto debate, programmers have been avoiding goto for some 40 years and therefore they aren't used to reading code with goto. This is a valid reason to avoid goto: it simply isn't the norm.

I would have rewritten the code as something more easily read by C programmers:

Error some_func (void)
{
  Error error;
  type_t* resource = malloc(...);

  error = some_other_func (resource);

  free (resource);

  /* error handling can be placed here, or it can be returned to the caller */

  return error;
}


Error some_other_func (type_t* resource)  // inline if needed
{
  error = function_that_could_fail_1();
  if(error)
  {
    return error;
  }

  /* ... */

  error = function_that_could_fail_2();
  if(error)
  {
    return error;
  }

  /* ... */

  return ok;
}

Advantages of this design:

  • The function doing the actual work does not need to concern itself with tasks that are irrelevant to its algorithm, such as allocating data.
  • The code will look less alien to C programmers, since they are afraid of goto and labels.
  • You can centralize error handling and deallocation at the same spot, outside the function doing the algorithm. It doesn't make sense for a function to handle its own results.
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.