The use of goto in C (not C++)

Started by
44 comments, last by Dmytry 15 years, 9 months ago
I'd prefer putting the upper part in a seperate function, however, a common way to avoid goto is abusing a switch or loop.

do {   if (bla)      break;   if (foo)      break;} while(0);//cleanupofswitch(whatever){  default:     if (bla)         break;     ...}



Frankly, I'd rather have a clear and simple "goto cleanup" than this form of raping unrelated constructs.. unless you could put the code in question in its own function and just return in different places, which I'd consider the "cleanest" solution.
f@dzhttp://festini.device-zero.de
Advertisement
(As long as we're talking C, here) I think a goto here or there can be of great help. But I wouldn't use more than one inside a single function -- that's when people start having to put in additional effort to understand what's going on. Your ultimate goal is to avoid that unneeded effort.

The whole point of using a goto (I assume) is for brevity because brevity often aids clarity. Too much brevity may have the opposite effect to that which is desired.

EDIT: by "one", I mean one *label*, not necessarily a single goto statement.
I spent a lot of years programming in FORTRAN IV, where GOTO was pretty much the only control flow mechanism (although, to be fair, there were two different kinds of GOTO, so there was a certain kind of richness to the language). I learned through maintaining a lot of other people's code that an excellent rule is to only ever jump forward. Jumping backwards makes spaghetti.

In C there is never a good reason to use goto: wise use of functions will almost always do a better job of making maintenance easier. That said, if you insist on writing idiomatic FORTRAN in C, make sure all your jumps go forwards.

Stephen M. Webb
Professional Free Software Developer

Quote:Original post by thedustbustr
Linux: Using goto In Kernel Code (traditional fun Linus rant).

Linux Device Drivers, 2nd Edition; Chapter 2: Building and Running Modules; Error Handling in init_module

Interesting read. Thanks for sharing.

I've always been a big "ZOMG I FUCKING HATE GOTOS, I WILL MURDER YOU IF YOU USE THEM" type of individual. However, I suppose that like all the language features, if abused and over used incorrectly, it results in terrible code. Those examples as presented in the quoted link really do seem to function better as gotos.

I definitely won't be in any hurry to use goto in my code though...
kernel coding isn't quite what I'm doing...
@Gage64:
void func() {    int some_error = 0;    int *arr = (int*)malloc(sizeof(int) * 50);    if (!arr) {        printf("Error allocating memory");        some_error = 1;    }    FILE *file = fopen("myfile.txt", "t");    if (!file) {        printf("Failed to open file");        some_error = 1;    }    if (! some_error) {        /* regular operation */    } else {        /* cleanup */        if(file) {            fclose(file);        }        if(arr) {            free(arr);        }    }

The code now reads from top to bottom and requires no backtracking to understand.
I love gotos, i just never have a chance to use it :( (thats good tho)

usually i do something like
{ long ret; FILE *f, *f2; f = fopen(...); if (f == 0) {  ret = -1;  goto errHdl; } f2 = fopen(...); if (! f2) //90% of times i do == but the odd time i like ! then a space) {  ret = -2;  goto errHdl; } ... ret = 0;errHdl: if (f2)  fclose(f2); if (f)  fclose(f); return ret;}


The other time i use goto is
 while (loopCond1) {   while (loopCond2)   {     if (x == OMG)     {       val = something;       goto breakOut;     }   } }breakout: some processing here related to loop above //then code for rest of the func, if applies


also, you may like alloca(); You dont need to free() it. The memory is on stack so its the equivalent of func(size_t size) { char arr[size]; } except sometimes your code is more complex. ex
long func(_t someStruct){ char *table[someStruct.length];  for (size_t i=0; i<someStruct.length; i++) {  if (someStruct.arr.val != 0)  {   table = alloca(someStruct.arr.len);   memcpy(table, someStruct.arr.val, someStruct.arr.len);  } } //process table}

I actually had to do something like that but much more complex. I was taking strings, arrays and ints and converting it to text which i then process with a tableToSql() function. Yikes! alloca was really nice to use there. Except in my case my tables were small enough to fit on the stack. At least for a while, i then later had added support for bin files and i easily replaced the alloca with malloc and add a delete at the end of the func. So the advantage to this is how easy you can convert it to malloc instead of doing some hack with a bunch of { char arr[XSize]; }
<SkilletAudio> Your framerate proves your lack of manhood
Sometimes its hard to deal with these cases where you have to release multiple resources.

Now you may use "C"'s ability to create dynamic arrays on stack:
I'm assuming you're doing this:
int *arr = (int*)malloc(sizeof(int) * 50);
because in reality '50' is a variable 'x', in which case you can do:
int arr[x];
C++ doesn't handle this, so you may not be able to compile it if you're using C++ compiler, or old C compiler.
Here's a full program that compiles:
#include <stdio.h>#include <stdlib.h>intmain(int argc, char **argv){        int arr[argc > 1 ? atoi(argv[1]) : 5];        printf("sizeof(arr) == %d\n", sizeof(arr));        return 0;}

Another 'trick', though I'm not in love with it, is:
void func() {    FILE *file = NULL;    int *arr = (int*)malloc(sizeof(int) * 50);    int got_error = 1;    if (!arr) {        printf("Error allocating memory");// you shouldn't free NULL, and // is a valid C comment now        return;    }    while (1){    file = fopen("myfile.txt", "r");    if (!file) {        printf("Failed to open file");        break;    }        if (some error) {        break;    }        if (some error)        break;        if (some error)        break;        /* ... */    got_error = 0;    break;}free(mem);if (file) fclose(file);if (got_error) fprintf(stderr, "I got error!!!!\n");return 0;}

(code is not indented because 'tab' doesn't seem to be working in this window :(

http://www.mildspring.com - developing android games

Quote:Original post by Oluseyi
@Gage64:
...
The code now reads from top to bottom and requires no backtracking to understand.


Like I said, my example was illustrative. You are assuming that you can perform further operations (attempt to open a file) even if previous operations failed (the memory allocation). In your code, that's true, but what if one operation depends on a previous operation being successful? If the previous operation failed, you cant continue - you have to exit the function immediately, while making sure to cleanup any allocated resources.

Still, it does show that elegantly avoiding a goto is not as hard as I thought in this particular case.

I'm not sure if ToohrVyk's approach suffers from the same problems or not. I wish I had a complicated real world example to test it with...
Quote:Original post by Gage64
Do you think the use of goto is justified here?


Not really. Lots of C programmers would use goto to handle this kind of thing, but they'd also generally structure it rather differently. :)

Quote:If not, how would you rewrite the code to avoid it?


/* NOTE: ONLY func() gets mentioned in the header file. Unless you have a good reason otherwise. :) */void func_mem_file(int* arr, FILE* file) {    if (some error) { return; }    /* ... */    if (some error) { return; }    /* ... */    if (some error) { return; }    /* ... */}void func_mem(int* arr) {    FILE *file = fopen("myfile.txt", "t");    if (file) { func_mem_file(arr, file); }    else { printf("Failed to open file"); }    fclose(file); /* this could instead go in the if-clause above. */}void func() {    int *arr = (int*)malloc(sizeof(int) * 50);    if (arr) { func_mem(arr); }    else { printf("Error allocating memory"); }    free(arr); /* this could instead go in the if-clause above. */}


Quote:Original post by AcidZombie24
I love gotos, i just never have a chance to use it :( (thats good tho)
...
The other time i use goto is


Wait, what?

@abeylin: free()ing NULL is fine, just like delete'ing NULL.

Quote:Original post by thedustbustr
Linux: Using goto In Kernel Code


Heh. IMX, people who defend goto in those sorts of threads generally don't know what they're talking about, and it's generally pretty transparent (at least to me) that this is the case. The last post shown, in particular, is gold:

Quote:
No, it is gross and it bloats the kernel.  It inlines a bunch of junkfor 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-esquewind 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



Er, you don't get "stack-esque wind and unwind" by calling one function from another (as in my above snippet)? Where does he think he got the term from?! He must be imagining (this is the only explanation I can think of) that every user of the code calls the allocation function, then the implementation function, then the cleanup function afterward. Not very imaginative, hmm?

Further, my technique offers the usual benefits of function decomposition: namely, (a) you can (in some cases; i.e. if your available resource set is a prefix of the total required set, in the order you choose to put them) reuse resources from other places if you happen to have them for some reason (and want to hang onto them afterwards, as you typically would in these cases); and (b) you can ignore the resource allocation code completely while debugging the implementation function.

Although I suppose it's bad form to speak ill of the dead (the thread, not the poster!)...

[Edited by - Zahlman on July 3, 2008 2:56:24 PM]
I want to make clear that when i used alloca, i meant alloca and NOT malloc. Malloc you free, alloca you dont. Malloc you can allocate tons of memory, alloca you cant (remember, its on the STACK).

Zahlman:
>Wait, what?

The 'other' time i use gotos, are when i need to break out of 2 loops. The break keyword unfortunately only breaks out of the current loop. I would like a break 2; keyword. (i never needed 3 but why not have it)
<SkilletAudio> Your framerate proves your lack of manhood

This topic is closed to new replies.

Advertisement