• Forum
  • Lounge
  • If Goto is "considered harmful," then so

 
If Goto is "considered harmful," then so can...

Pages: 123
I can't find some web article I glanced over long ago which argued something like: if the goto statement is "considered harmful," then so can all the other control structures be "considered harmful." They weren't only justifying goto's occasional use (i.e. exiting nested loops etc.), but they also (I think) showed how commonplace it has become for complex procedures and seemingly "elusive" systems to be bloated and overcomplicated by programmer's "blindingly" strong familiarity with arbitrary control-structure primitives such as if/else, switch, while, for etc.

I'm extremely interested with this sort of thought, at the moment. Do you have any thoughts to add? Have you ever seen this argument before?
Last edited on
Never heard about such argument before. All I have ever heard about goto is bad things, one very specific approved case of use and some hard-to-believe statement I'd rather not mention here. All that you heard is nonsense to me.

The use of goto is related to spaghetti code. See http://en.wikipedia.org/wiki/Spaghetti_code .
More traditional {block} approaches are quickly identifiable. Once you see a { you can quickly notice the matching } to know what all code is affected. You also can see that there's a clear entry point, and what kind of block it is (if/for/while/etc)

With goto you can't see any of that. You can't know what section of the code will jump to a label without reading each and every line of code in the function. There are no clear boundaries to the affected code, because there are no boundaries for goto.

I just went over this in another thread also:

http://cplusplus.com/forum/general/77266/#msg415740
@webJose
All I have ever heard about goto is bad things

As if I haven't? Open up now.

"Spaghetti code." Yes, but the argument was about how our given control structures are also capable of rendering spaghetti structure alike the goto statement. Neither I or the article are suggesting any solution. I was interested in rediscovering the article's non-vague examples which corresponded to this point (which I recall seeing).

All that you heard is nonsense to me.

That's too bad. You should comprehend how "then so can all the other control structures ..." does not equate to "but using goto can sometimes be a very good practice in comparison to common control structures."

Essentially, this topic isn't about goto in particular. Not at all! This topic is for the sake of abstractly discussing control structures and also the structure they imply beyond program state.
Last edited on
While I have yet to encounter a case where I felt something like goto was necessary, I havn't simply written it off entirely because it's the general consensus to not use it. There's rarely a "one size fits all" when it comes to programming and as a result it's rather likley that there are situations where goto is the best solution. However if the programmer in question has completely written it off for some reason they'll ultimately wind up using a solution that is in some degree inferior.

So with that said I can see where that argument may be coming from. I don't think it would be a case that would be encountered very frequently, but I think it still has a valid idea (think of how many programmers abuse templates, using them where they really don't need them).

Ben Bowen wrote:
"Spaghetti code." Yes, but the argument was about how our given control structures are also capable of rendering spaghetti structure alike the goto statement.
I disagree, the control structures do exactly as the name implies and help control the structure of the code. They have a single entry point and exit and flow top down. The only way I can think of to create spaghetti code with control structures off the top of my head would be an insane amount of nested function calls, and even then the control will eventually return to the flow of the program. IMHO goto's should be used extremely sparingly like a deeply nested loop and then should only go with the flow of the program (down) and not very far. Unless of course your using asm or basic with little to no control structures (which you an easily simulate with goto/jump).
Last edited on
@Ben Bowen: Why the attitude towards my response? I am giving you my honest opinion about goto and what you posted as someone else's words. Yes, I have only ever heard bad things about the use of goto. How's that being closed? I am describing my experience.

You are also speaking of and making reference to an article that so far only you have read. If you posted a link to it maybe you can get more profound comments. All we have is your summary.

And yes, your summary of the article is all nonsense to me. That's my opinion. It is not an attack of any kind towards anybody. I don't have to agree with every article that is out there, do I?

Ben Bowen wrote:
Neither I or the article are suggesting any solution.


I don't see why mention this to me. I never suggested that you or the article suggested. :-P
three things. number one i haven't read the whole article so i dont know if this was mentioned but i read that the reason goto is bad because it was used in basic and i think batch becuase there was no other way and now there is. two: there is a programming interview book where this one great programmer defends goto. third google search comefrom
Every control structure is turned into goto (or equivalent) by the compiler.

For example, the code
1
2
3
4
5
if (variable == value)
    do_something();
else
    do_something_else();
do_something_anyway()

might compile to something like
1
2
3
4
5
6
7
8
9
10
11
12
13
L0:
    // Compare variable and value, jump to L2 if not equal
    cmp variable, value
    jne L1 // conditional jump (jump if not equal)
    // Executed only if variable == value
    call do_something
    jmp L2 // (unconditional jump (equivalent to goto)
L1:
    // Executed only if variable != value
    call do_something_else
L2:
    // Executed always
    call do_something_anyway


break and continue statements are also turned into unconditional jumps:
1
2
3
4
5
6
while (1) {
    if (condition)
        break;
    else
        continue; // Unnecessary, but kept for demonstration's sake
}

might become
1
2
3
4
5
6
7
8
9
10
11
12
L0:
    // if condition == 1 (true)
    cmp condition, 1
    jne L02
    L01:
        jmp L1 // break
    L02:
        jmp L0 // continue
    cmp 1, 1 // If the compiler is really stupid, it might turn while (1) into this
    je L0 // Next iteration
L1:
    // End of loop 
Last edited on
@chrisname
I am sure you know all this stuff backwards & inside out (I am guessing you have vastly more knowledge & experience than me), but here goes anyway.

Of course you are right in your example of assembler. However there is a huge difference between that and almost every other language.

Assembler doesn't have any looping structures apart from jmp, that is the nature of assembler.

Languages of a higher level than assembler have control flow structures to make it easier to use & understand - that is the nature of higher level languages.

I am not sure what you are trying to get at with your post, I wonder is it an example of the type of argument that the OP was talking about?

Ikaron wrote:
(think of how many programmers abuse templates, using them where they really don't need them).


Yes, this may be more of what the topic is about.

naraku9333 wrote:
IMHO goto's should be used extremely sparingly like a deeply nested loop and then should only go with the flow of the program (down) and not very far.


That nails it I think on the use of a goto.


Here's another question: I wonder how much code would be broken if goto was deprecated?

I wasn't really "getting at" anything. I agree that goto's use should be restricted, but I don't agree that it should never be used. There are some cases when an unconditional jump is better than an alternative; for example, if you need to exit a function from several different locations and do some common clean-up (release memory and such) before exiting, the best way to do that is to use something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int foo()
{
        ...
        if (error1) {
                fprintf(stderr, "Error 1 occurred!\n");
                goto leave;
        }
        ...
        if (error2) {
                fprintf(stderr, "Error 2 occurred!\n");
                goto leave;
        }
        ...
        if (error3) {
                fprintf(stderr, "Error 3 occurred!\n");
                goto leave;
        }
        ...
        free(resource1);
        free(resource2);
        free(resource3);
        return 0;
leave:
        free(resource1);
        free(resource2);
        free(resource3);
        return -1; // Error!
}

is better than doing
1
2
3
        free(resource1);
        free(resource2);
        free(resource3);

In every if statement where an error occurs.

I've heard that in some cases goto can also help the compiler with optimisation, but that's just hearsay.
Last edited on
@chrisname

I wasn't really "getting at" anything.


Sorry I didn't mean 'getting at' in a negative sense - I should have asked what you 'meant' instead.



@Chrisname, how does this look?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int foo()
{
        ...
	bool error = false;
        if (error1) {
                fprintf(stderr, "Error 1 occurred!\n");
                error = true;
        }
        ...
        if (error2) {
                fprintf(stderr, "Error 2 occurred!\n");
                error = true;
        }
        ...
        if (error3) {
                fprintf(stderr, "Error 3 occurred!\n");
                error = true;
        }
        ...
		
        free(resource1);
        free(resource2);
        free(resource3);
        if(error) 
		return -1;
	return 0;
}
@TheIdeasMan,
No, that's what I meant: I wasn't making an argument.

@xander337,
That's probably better than using goto.
webJose wrote:
Never heard about such argument before. All I have ever heard about goto is bad things, one very specific approved case of use and some hard-to-believe statement I'd rather not mention here. All that you heard is nonsense to me.


It's strange you've never heard such an argument before, as it has some very good points! Sometimes goto is appropriate, although rarely. And a lot of the time people misuse control structures, which could be replaced by a simpler form using the control structures a different way (or by goto statements, but it's unlikely that would make it simpler).

Of course you've heard many bad things about goto, though; it's 99.6% of the time a terrible idea in well-written code!

Disch wrote:
More traditional {block} approaches are quickly identifiable. Once you see a { you can quickly notice the matching } to know what all code is affected. You also can see that there's a clear entry point, and what kind of block it is (if/for/while/etc)

With goto you can't see any of that. You can't know what section of the code will jump to a label without reading each and every line of code in the function. There are no clear boundaries to the affected code, because there are no boundaries for goto.


Yes, that is of course why control structures are used. They provide a similar kind of functionality to what was already used all the time in programs with jump statements, and clearly show program flow compared with goto and labels.

Ben Bowen wrote:
"Spaghetti code." Yes, but the argument was about how our given control structures are also capable of rendering spaghetti structure alike the goto statement.


As I said above, I totally agree it's easy to make bad code with control structures, although easier with goto statements.

naraku9333 wrote:
I disagree, the control structures do exactly as the name implies and help control the structure of the code. They have a single entry point and exit and flow top down.


I think they help, but they can be misused; I believe you're wrong in saying that just because something is effective it means it can't be used incorrectly to disadvantage!

chrisname wrote:
I agree that goto's use should be restricted, but I don't agree that it should never be used. There are some cases when an unconditional jump is better than an alternative; for example, if you need to exit a function from several different locations and do some common clean-up (release memory and such) before exiting, the best way to do that is to use something like this:

...


I do the same thing in my code a lot, too! You could easily do it in the control structures too, but it makes it look more complicated, in my opinion, and harder to understand. This is a very nice example of where goto is appropriate (although this code is so simple and unrestrictive, it could in fact be done with more appropriate control structure, but I imagine it 5 times as long, and with the freeing of resources as an inline function).

EDIT: It seems everyone here agrees that goto is a bad idea most of the time, but some people would say it's not ever worth using and some would say it can be cleaner than control structures at points...
Last edited on
I've yet to find a situation where using goto could be used where a conditional block wouldn't work just as well if not better.

That is of course not including scripts and languages that lack control structures.
Veltas wrote:
It's strange you've never heard such an argument before, as it has some very good points! Sometimes goto is appropriate, although rarely.

How is that strange? Look around and tell me what you see. How many proponents of goto are you finding? How many opponents? Statistically speaking it is not strange at all.

I also already conceided ONE very specific case for goto: Exit out of multiply-nested loops all at once (granted, this is the first time I spell it out). That's it. You said "Sometimes goto is appropriate, although rarely.". Are you referring to this case, or others? Like I said before, I personally don't know of any other "approved" case for this primitive and outdated instruction, that yes, is all over the place in assembly, but thankfully we now have higher-level languages that shield us from the horrors of goto.

I'm a little afraid to ask about other use cases because last time it ended up pretty badly, but here goes: If you have other use cases in mind for goto, post them for the sake of discussion.
@xander337,
I just realised why your example doesn't work: if an error occurs, it keeps executing the code that follows the 'if' block. If you were, say, opening a file or allocating memory, and you were checking to make sure the file opened or the buffer was allocated correctly, you would (potentially) find out it failed, and then go on and use it anyway.

This would be better (still using goto):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int foo()
{
	int error = 0;
        ...
        if (error1) {
                fprintf(stderr, "Error 1 occurred!\n");
				error = 1;
                goto leave;
        }
        ...
        if (error2) {
                fprintf(stderr, "Error 2 occurred!\n");
				error = 1;
                goto leave;
        }
        ...
        if (error3) {
                fprintf(stderr, "Error 3 occurred!\n");
				error = 1;
                goto leave;
        }
        ...
leave:
        free(resource1);
        free(resource2);
        free(resource3);
	if (error)
		return -1;
	return 0;
}


Veltas wrote:
I do the same thing in my code a lot, too! You could easily do it in the control structures too, but it makes it look more complicated, in my opinion, and harder to understand. This is a very nice example of where goto is appropriate (although this code is so simple and unrestrictive, it could in fact be done with more appropriate control structure, but I imagine it 5 times as long, and with the freeing of resources as an inline function).

Exactly. Using control structures can be uglier, longer, more complex and more confusing, which is exactly why people say not to use goto in the first place.
Last edited on
I used to do the whole 'goto to clean up and exit with error' back before I discovered RAII. If you are using RAII then you don't need to do that because the resources will be freed when they go out of scope anyway.

So in C, it makes a lot of sense, but in C++ it's kind of silly.

The other alternative to that is exceptions. I find myself hardly ever writing functions which return error codes anymore. Checking return codes for errors is a huge pain in the arse.

chrisname's example with RAII + exceptions:

1
2
3
4
5
6
7
8
9
10
void foo()
{
  ...
  if(error1)    throw MyException("Error 1 occurred!");
  ...
  if(error2)    throw MyException("Error 2 occurred!");
  ...
  if(error3)    throw MyException("Error 3 occurred!");
  ...
}


EDIT:

or, if you prefer returning an error and don't want the exceptions to carry forward:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int foo()
{
  try
  {
    ...
    if(error1)    throw "Error 1 occurred!";
    ...
    if(error2)    throw "Error 2 occurred!";
    ...
    if(error3)    throw "Error 3 occurred!";
    ...
  }
  catch(const char* errmsg)
  {
    fprintf(stderr, errmsg);
    return -1;
  }
  return 0;
}
Last edited on
Similarly to breaking out of nested loops, you can use goto to continue an outer loop while executing an inner loop, like this:

1
2
3
4
5
6
7
8
9
10
11
12
while(condition)
{
    ...
    for(...)
    {
        ...
        if(condition) goto while_end; //continue would not work here
        ...
    }
    ...
    while_end:;
}


The cleanest alternative is to put the body of the while loop inside a function, which may or may not be cleaner than this solution.
Last edited on
Pages: 123