Sign in to follow this  

Quick question about goto in C++

This topic is 4521 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Maybe people will recognize me from my ill-informed python vs. c++ post in For Beginners. Hehe, had to start somewhere. Anyway, I decided I'd give C++ a little more of a solid shot, and in my studies, I encountered the goto command, which sounds real nice n' all, but the tutorial/book I'm reading recommends against using it, as goto is 'considered a poor programming style.' Not much explanation was given, so I'm curious why it's favorable to use loops instead of goto. Thanks in advance, to any potential answerers :D

Share this post


Link to post
Share on other sites
Readability: each of the loops (while, do while, for) has well-known and explicit functioning, and you know just by looking at the indentation what block of code is "inside" the loop, as well as when and how it will be executed.

On the contrary, goto has a much more wide range of possibilities, which means that upon encountering a goto in code, the reader will have to spend some time figuring out what the code does exactly. There is also the possibility of error while reading or writing the code, because more things can go wrong with goto than with a loop.

This does not exclude the use of goto in situations where this use is widely known and accepted in the industry, such as error management in C (by having all errors go to a portion of code at the end of the function that cleans up the data).

I do not know of any widely known and accepted use of goto in C++, however, so by using it you expose yourself to the risk of making your code more unreadable and bug-prone than if you had gone with loops.

Share this post


Link to post
Share on other sites
There's a story -- not sure how true it is -- that in the first version of Java, if you used goto, the following error message would appear:

KeywordTest.java:4 'goto' not supported. Duh.
You have no business programming in Java. Begin erasing
Java Software Development Kit? (Yes/OK)
1 life altering error

says it all, really... [grin]

Share this post


Link to post
Share on other sites
I agree with you, but in a situation like this it becomes pretty useful:

while (...)
{
while (...)
{
if (...)
goto endWhiles;
}
}
endWhiles:


I know there are other ways to get around this but this seems the simplest.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I'm not sure if it is the same in C++, but while coding it is best to use loops because the computer stores the start and end of the loop, making it very fast to keep running the loop until conditions have been met. When a goto comes up the computer has to look through the code to find where you are looking for, which takes up a bit more time than a loop would. It also seems to be prone to small memory leaks which can further slow down your code.

Share this post


Link to post
Share on other sites
Not trying to re-ignite a recent flamewar, but a goto is handy as a multilevel break, among other things.

int x,y,z;
for(x=0;x<100;++x)
{
for(y=0;y<100;++y)
{
for(z=0;z<100;++z)
{
foo(x,y);
if(bar(x,y)) goto loop_done;
quux(x,y);
}
}
}
loop_done:


They are also extremely useful is machine-generated code. In particulatr, parser generators (like bison) which translate the grammar into a state machine, and the state machine into actual C code - riddled with gotos.

As for goto being evil, well... people are taught that gotos are evil, therefore they don't use them, thus they don't learn to use them. So when they encounter code that includes a gotom they are baffled and, as ToohrVyk pointed out, are forced to think about how the goto work. The readability of the code is thus reduced, before you even start considering whether the use of the goto was justified or not (which they are told is never the case). It's a self-fulfilling prophecy and a self-propagating meme rolled into one neat package. You accept what you were told at face value (or get graded down in your assignments) and in turn, repeat what you were told to new programmers without accounting for changing circumstances (and without updating their skills).

There is also a fear of "spaghetti code", where gotos jump all over the place. Forty years ago, that was a valid concern, considering both the actual languages in use (e.g. having line numbers mean you can potentially do a goto to any point in the program without having any hint at the destination line that there is code that jumps here or not) and the tools available (modern IDE = gold). Fortunately, in C++, you are limited to local gotos (can't leave the function), so any potential spaghetti code is limited in scope.

Beyond that, it's a question of discipline and of doing the Right Thing, just like public/private members, macros, multiple inheritance, fixed-size buffers, variadic functions, virtual functions, exceptions, operator overloading... there are plenty of controversial features in the languages, most of which can be misused or used constructively. The designers of Java chose not to include operator overloading because (generally speaking) someone, somewhere, might define + to mean -. Can you? Yes. Should you? No.

Can a goto be useful? Yes. Should it be the first thing you reach for? No.

Assignment: What were the popular languages when Dijkstra's paper was written? Reframe the goto question in the context of those languages. Compare and contrast with current programming techniques, paradigms and languages. You have 180 minutes. [wink]

Share this post


Link to post
Share on other sites
Quote:
Original post by 999999999
I agree with you, but in a situation like this it becomes pretty useful:

while (...)
{
while (...)
{
if (...)
goto endWhiles;
}
}
endWhiles:



I would rewrite this code using a function around the outer while. Not because there is a goto in it, but because in 99.9% of while loops the only ways to get out is:
- a break in the loop
- the condition becoming false
- the function returning

(Also, exceptions, but they do not interfere with the algorithm since they skip everything back to a safe state).

Your code is the 0.1% situation where an additional case has to be considered, and it's the kind of thing that is tough on the maintenance programmers, especially when they are not used to this control structure.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
When a goto comes up the computer has to look through the code to find where you are looking for, which takes up a bit more time than a loop would.


No. This is resolved at compile time. Furthermore, internally, a for-loop really is just a increment/test/goto package deal. You can have cache misses with a for-loop just as well as with a goto.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
They are also extremely useful is machine-generated code. In particular, parser generators (like bison) which translate the grammar into a state machine, and the state machine into actual C code - riddled with gotos.


The only limitation on goto being that of being hard to parse and manipulate (but not create) by humans, using it in machine-generated code is an all-win situation.

Quote:

Assignment: What were the popular languages when Dijkstra's paper was written? Reframe the goto question in the context of those languages. Compare and contrast with current programming techniques, paradigms and languages. You have 180 minutes. [wink]


PDP-10 assembler and LISP? What do you mean, wrong century?

Share this post


Link to post
Share on other sites
More formally :

Languages like C/C++ and Java are known as structured languages - they are based around structured blocks of code for/while loops if statements etc where the points of entry and exit are defined at the top/bottom of the block. The thing with gotos (and breaks and continues in loops) is that you can break the structuredness of your code by jumping out in the middle of a block of code.

As the a previous post said :

The main problems with unstructured code are that it makes code harder to read and harder to refactor.

Gotos are particularly dangerous because you can basically jump anywhere in a program in an unstructured way which makes bugs more likely (and harder to detect)

There are certain uses of gotos (depending on who you speak to) which are acceptable - for example to simulate exceptions (like in C++) or On Error in VB in languages that don't support such a concept directly (like in C). But you have to pay a lot of attention to how you do it

Share this post


Link to post
Share on other sites
Which is more readable:

void DoSomething(Object* ParentObject)
{
int WithSomething = 5;
do
{
Action* MyAction = DoStuff(WithSomething);
ParentObject->DoAction(MyAction);
MyAction->Update();
if(ParentObject->SomethingHappens())
{
WithSomething = 10;
continue;
}
} while(0);
}


or

void DoSomething(Object* ParentObject)
{
int WithSomething = 5;
MyLabel:
Action* MyAction = DoStuff(WithSomething);
ParentObject->DoAction(MyAction);
MyAction->Update();
if(ParentObject->SomethingHappens())
{
WithSomething = 10;
goto MyLabel;
}
}



You're free to disagree with me, but the first version seems more obfuscated and hackish than clear and correct.

Share this post


Link to post
Share on other sites
Yes it is, it's a while(0), not a while(1), it caught me too for a bit.

It should be rewritten using a flag for the termination condition, assigned with SomethingHappens()'s return value.

Share this post


Link to post
Share on other sites
I may be wrong, but this looks suspiciously like a loop-with-exit[1]. Cleaner would be:


void DoSomething(Object* ParentObject)
{
int WithSomething = 5;
for (;;) // Or some kind of FOREVER macro
{
Action* MyAction = DoStuff(WithSomething);
ParentObject->DoAction(MyAction);
MyAction->Update();

if(!ParentObject->SomethingHappens())
break;

WithSomething = 10;
}
}



[1] See Code Complete

Share this post


Link to post
Share on other sites
In that particular case I would've used while(1) { ... if ... else break;} which would doubtless compile to identical or near-identical code, but I think is clearer. I'd say gotos can be useful but I don't see any good reason they should be used for constructing loops.

Edit: Ah, beaten to it.

Share this post


Link to post
Share on other sites
Again, you're free to disagree, but to me that looks like a hackish way to avoid a goto.


void DoSomething(Object* ParentObject)
{
int WithSomething = 5;
MyLabel:
Action* MyAction = DoStuff(WithSomething);
ParentObject->DoAction(MyAction);
MyAction->Update();
if(ParentObject->SomethingHappens())
{
if(ParentObject->Happened() == AC_DidntWork)
{
WithSomething = 10;
goto MyLabel;
}
else if(ParentObject->Happened() == AC_Blah)
{
ParentObject->DoNextAction();
}
else if(ParentObject->Happened() == AC_Foo)
{
ParentObject->Kill();
}
else if(ParentObject->Happened() == AC_Bar)
{
ParentObject->DoSomethingElse();
}
else
{
ParentObject->FlagError();
}
}
ParentObject->Update();
}




or:

void DoSomething(Object* ParentObject)
{
int WithSomething = 5;
while(true)
{
Action* MyAction = DoStuff(WithSomething);
ParentObject->DoAction(MyAction);
MyAction->Update();
if(ParentObject->SomethingHappens())
{
if(ParentObject->Happened() == AC_DidntWork)
{
WithSomething = 10;
continue;
}
else if(ParentObject->Happened() == AC_Blah)
{
ParentObject->DoNextAction();
}
else if(ParentObject->Happened() == AC_Foo)
{
ParentObject->Kill();
}
else if(ParentObject->Happened() == AC_Bar)
{
ParentObject->DoSomethingElse();
}
else
{
ParentObject->FlagError();
}
}
ParentObject->Update();
break;
};
}



Share this post


Link to post
Share on other sites
I personally would have written it:
void DoSubSomething(Object* ParentObject, int Parameter)
{
Action* MyAction = DoStuff(Parameter);
ParentObject->DoAction(MyAction);
MyAction->Update();
return ParentObject->SomethingHappens();
}

void DoSomething(Object* ParentObject)
{
if(DoSubSomething(ParentObject, 5))
{
while(DoSubSomething(ParentObject, 10))
{
// empty
}
}
}

or
void DoSomething(Object* ParentObject)
{
bool ContinueProcessing = DoSubSomething(ParentObject, 5);
while(ContinueProcessing)
{
ContinueProcessing = DoSubSomething(ParentObject, 10);
}
}

Both of which emphasise the high-level goal of doing something once and, if that passes, repeating that something multiple times with a different parameter. The code posted so far seem to me to emphasise the method of doing something, with the higher-level goal being hidden away as a side-effect.

Enigma

Share this post


Link to post
Share on other sites
Anyway, I guess this successfully demonstrates that a goto is just one of many way of (poorly? [grin]) writing a loop.

Share this post


Link to post
Share on other sites
While goto code can be harder to track from the human point of view,
it is true that the compilers do use machine code "goto" instruction to build loops.

The real problems that come from "goto" usage are:
1. break of structural paradigm
2. possible corruption of the stack
3. CPU branch predictability failure

The truth is that there are very little, if any, algoritmic pieces that
cannot be written using strictly structured code.

"while(0)" or the kind is not very good structured code as long as you still need to evaluate the termination condition.

My personal prefered way of exiting loops is by setting a controlling variable to an overshot value. This way I can tell why the loop ended more easyly.

But since GOTO is still a suported instruction, there is no reason why not to actually use it, as long as you can cope with the results.

Share this post


Link to post
Share on other sites

This topic is 4521 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this