Jump to content

Why do people not recommend using the goto command?

dcgreen2k

As a beginner programmer, I think that goto is pretty useful, but people don't recommend using it. Is it because it's easy to exploit if someone gains access to the code, or is it as simple as there being a function for it?

Computer engineering grad student, cybersecurity researcher, and hobbyist embedded systems developer

 

Daily Driver:

CPU: Ryzen 7 4800H | GPU: RTX 2060 | RAM: 16GB DDR4 3200MHz C16

 

Gaming PC:

CPU: Ryzen 5 5600X | GPU: EVGA RTX 2080Ti | RAM: 32GB DDR4 3200MHz C16

Link to comment
Share on other sites

Link to post
Share on other sites

It's generally because it's way too easy to fall into the habit of just always using goto and then you sooner or later end up with practically unreadable spaghetti-code. An experienced coder can use goto, but they know to use it sparingly, whereas a beginner tends to go overboard with it. It's best to first teach oneself the habit of not using goto, and only when one is experienced then possibly use goto sparingly and in carefully-considered places.

 

This is to say, it's nothing to do with exploits, it's all about producing code that is easier to maintain and extend in the future -- especially so, if you're making a large project.

Hand, n. A singular instrument worn at the end of the human arm and commonly thrust into somebody’s pocket.

Link to comment
Share on other sites

Link to post
Share on other sites

In C++, goto can be quite dangerous because it is just implemented in assembly as a jump, so things like variable initialisation and stack creation are just skipped over. If you only jump within a scope, this is ok, but if you jump say from one if block to another it can cause issues I believe, and certainly jumping to a different function.

When used carefully, goto can be helpful, and in theory it's intuitive for people to learn, but the risk of it not working as expected for a new programmer makes it inadvisable.

 

nb. I don't have sources for this information, this is just based on my understanding of it based on information that I've seen before.

HTTP/2 203

Link to comment
Share on other sites

Link to post
Share on other sites

7 hours ago, thegreengamers said:

Is it because it's easy to exploit if someone gains access to the code, or is it as simple as there being a function for it?

Exploiting is all about two things:

  1. Get arbitrary data in range of a jump of any kind
  2. Control the jump to go to your data

Using goto may or may not cause a vulnerability. If you goto a variable that is not protected on the stack, then yes that could cause an issue because a goto is:

6 hours ago, colonel_mortis said:

just implemented in assembly as a jump

e.g.:

jmp 0xdeadbeef

I am talking about a situation like:

lea ebx, 0xdeadbeef; 0xdeadbeef = function pointer
push ebx; put it on the stack
mov eax, 0x4; buffer size
push eax; put it on the stack
mov edi, bufferName; destination (not ASM correct)
push edi; put it on the stack
call strcpy; user inputs ASCII equivalent of "\x41\x41\x41\x41\xbe\xba\xfe\xca", overwriting ebx stack value from 0xdeadbeef to 0xcafebabe
pop edi; get edi off stack
pop eax; get eax off stack
pop ebx; get ebx off stack
call ebx; ebx now contains 0xcafebabe which might be the address to a shell function that the user now gets

More commonly, goto is just not needed for most things (in my experience). I have been able to do everything I need to with loops and conditionals. I never really need to hard code a change in execution.

Join the Appleitionist cause! See spoiler below for answers to common questions that shouldn't be common!

Spoiler

Q: Do I have a virus?!
A: If you didn't click a sketchy email, haven't left your computer physically open to attack, haven't downloaded anything sketchy/free, know that your software hasn't been exploited in a new hack, then the answer is: probably not.

 

Q: What email/VPN should I use?
A: Proton mail and VPN are the best for email and VPNs respectively. (They're free in a good way)

 

Q: How can I stay anonymous on the (deep/dark) webzz???....

A: By learning how to de-anonymize everyone else; if you can do that, then you know what to do for yourself.

 

Q: What Linux distro is best for x y z?

A: Lubuntu for things with little processing power, Ubuntu for normal PCs, and if you need to do anything else then it's best if you do the research yourself.

 

Q: Why is my Linux giving me x y z error?

A: Have you not googled it? Are you sure StackOverflow doesn't have an answer? Does the error tell you what's wrong? If the answer is no to all of those, message me.

 

Link to comment
Share on other sites

Link to post
Share on other sites

Goto... just no, do not ever ever use it.

11 hours ago, WereCatf said:

An experienced coder can use goto, but they know to use it sparingly, whereas a beginner tends to go overboard with it.

11 hours ago, WereCatf said:

...only when one is experienced then possibly use goto sparingly and in carefully-considered places.

As an experienced coder as you put it (and I do hate that term with passion) I most certainly will do everything that I possibly can not to use it. In my opinion and experience if one is found to be asking oneself the question of 'should I use goto' then the problem lies in either the design or in one's own competence. Either of which should result in a considerable self flagellation!

9 hours ago, colonel_mortis said:

...goto can be helpful, and in theory it's intuitive for people to learn...

Which applies many anti patterns and bad practices...

3 hours ago, LtStaffel said:

More commonly, goto is just not needed for most things (in my experience). I have been able to do everything I need to with loops and conditionals. I never really need to hard code a change in execution.

Exactly.

The single biggest problem in communication is the illusion that it has taken place.

Link to comment
Share on other sites

Link to post
Share on other sites

2 hours ago, Nuluvius said:

Goto... just no, do not ever ever use it.

As an experienced coder as you put it (and I do hate that term with passion) I most certainly will do everything that I possibly can not to use it. In my opinion and experience if one is found to be asking oneself the quest of 'should I use goto' then the problem lies in either the design or in one's own competence. Either of which should result in a considerable self flagellation!

I don't think that's a very reasonable thing to say, either. Take a look at e.g. the Linux-kernel: there are tens of thousands of goto-statements in the code and oftentimes for a good reason, as there's far less overhead in using goto than in using function-calls, and thus it can make sense to use it in e.g. drivers, the task-scheduler and such things.

Hand, n. A singular instrument worn at the end of the human arm and commonly thrust into somebody’s pocket.

Link to comment
Share on other sites

Link to post
Share on other sites

14 minutes ago, WereCatf said:

I don't think that's a very reasonable thing to say, either. Take a look at e.g. the Linux-kernel: there are tens of thousands of goto-statements in the code and oftentimes for a good reason, as there's far less overhead in using goto than in using function-calls, and thus it can make sense to use it in e.g. drivers, the task-scheduler and such things.

In such very specific use cases yes there's validity. However those are realms far distant from where a beginner should be.

The single biggest problem in communication is the illusion that it has taken place.

Link to comment
Share on other sites

Link to post
Share on other sites

Just now, Nuluvius said:

In such very specific use cases yes there's validity. However those are usually realms far distant from where a beginner should be.

Well, if you'd take a look at my original comment, that's exactly what I said.

Hand, n. A singular instrument worn at the end of the human arm and commonly thrust into somebody’s pocket.

Link to comment
Share on other sites

Link to post
Share on other sites

22 minutes ago, WereCatf said:

Well, if you'd take a look at my original comment, that's exactly what I said.

Well, if you paid closer attention to mine then you'd notice that I wasn't completely disagreeing with you:

3 hours ago, Nuluvius said:

I most certainly will do everything that I possibly can not to use it.

To be fair, I think that a better distinction to make would be to take my post in a general context.

The single biggest problem in communication is the illusion that it has taken place.

Link to comment
Share on other sites

Link to post
Share on other sites

If you want to use gotos use assembly. Else convince yourself that gotos don't exist. 

 

That said when I was learning Python (my first language) I didn't know enough to efficiently control code flow so I considered gotos to be the best option. Then I realized functions and while/for loops are a thing and the need for a goto faded away. 

Link to comment
Share on other sites

Link to post
Share on other sites

5 minutes ago, ElfFriend said:

I didn't know enough to efficiently control code flow so I considered gotos to be the best option. Then I realized functions and while/for loops are a thing

I've used those before with "x++", but how could you create an infinite loop with them?

Computer engineering grad student, cybersecurity researcher, and hobbyist embedded systems developer

 

Daily Driver:

CPU: Ryzen 7 4800H | GPU: RTX 2060 | RAM: 16GB DDR4 3200MHz C16

 

Gaming PC:

CPU: Ryzen 5 5600X | GPU: EVGA RTX 2080Ti | RAM: 32GB DDR4 3200MHz C16

Link to comment
Share on other sites

Link to post
Share on other sites

2 hours ago, thegreengamers said:

I've used those before with "x++", but how could you create an infinite loop with them?

while(true){
 //infinite loop
 //not: it's considered bad practice to use break; everywhere.
  
}

 

Link to comment
Share on other sites

Link to post
Share on other sites

8 hours ago, ElfFriend said:

Else convince yourself that gotos don't exist. 

That's (generally) a very good ethic to follow.

5 hours ago, ElfFriend said:

//not: it's considered bad practice to use break; everywhere.

It depends. Preconditions and Guard Clauses are the place for these (break, continue, exit/return). This reduces nesting thus improving Cyclomatic Complexity and readability.

The single biggest problem in communication is the illusion that it has taken place.

Link to comment
Share on other sites

Link to post
Share on other sites

As an example of where goto is used is when you allocate resources but you have to clean them up before the function returns. If there's a problem in the function that prevents it from continuing, you also don't want to return immediately. Gotos will help eliminate if-statement nesting and helps with readability. Goto may also have the added benefit of benefit of being faster (it's an unconditional jump, i.e., set the program counter to a new value). The Linux kernel source uses this rather often.

 

An generic example of this and I stole it from https://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c, showcases that goto can make the code more clean

int foo(int bar)
{
    int return_value = 0;

    allocate_resources_1();

    if (!do_something(bar))
        goto error_1;

    allocate_resources_2();

    if (!init_stuff(bar))
        goto error_2;

    allocate_resources_3();

    if (!prepare_stuff(bar))
        goto error_3;

    return_value = do_the_thing(bar);

error_3:
    cleanup_3();
error_2:
    cleanup_2();
error_1:
    cleanup_1();
    return return_value;
}

You can rewrite this without gotos by doing:

int foo(int bar)
{
    int return_value = 0;

    allocate_resources_1();

    if (do_something(bar))
    {
        allocate_resources_2();

        if (init_stuff(bar))
        {
            allocate_resources_3();

            if (prepare_stuff(bar))
            {
                return_value = do_the_thing(bar);
            }

            cleanup_3();
        }

        cleanup_2();
    }

    cleanup_1();

    return return_value;
}

But then this looks like the Pyramid of Doom

 

Torvalds stresses readability over things like "not using gotos because blah blah blah". In this case, it's much easier to read and understand the function with the gotos than the if-statement pyramid and they perform exactly the same function.

 

EDIT: I would like to stress this is a very specific use-case. If you start seeing this pattern but you can return or break early without issues, then you shouldn't use goto.

Link to comment
Share on other sites

Link to post
Share on other sites

8 hours ago, M.Yurizaki said:

As an example of where goto is used is when you allocate resources but you have to clean them up before the function returns. If there's a problem in the function that prevents it from continuing, you also don't want to return immediately. Gotos will help eliminate if-statement nesting and helps with readability. Goto may also have the added benefit of benefit of being faster (it's an unconditional jump, i.e., set the program counter to a new value). The Linux kernel source uses this rather often.

 

An generic example of this and I stole it from https://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c, showcases that goto can make the code more clean


int foo(int bar)
{
    int return_value = 0;

    allocate_resources_1();

    if (!do_something(bar))
        goto error_1;

    allocate_resources_2();

    if (!init_stuff(bar))
        goto error_2;

    allocate_resources_3();

    if (!prepare_stuff(bar))
        goto error_3;

    return_value = do_the_thing(bar);

error_3:
    cleanup_3();
error_2:
    cleanup_2();
error_1:
    cleanup_1();
    return return_value;
}

You can rewrite this without gotos by doing:


int foo(int bar)
{
    int return_value = 0;

    allocate_resources_1();

    if (do_something(bar))
    {
        allocate_resources_2();

        if (init_stuff(bar))
        {
            allocate_resources_3();

            if (prepare_stuff(bar))
            {
                return_value = do_the_thing(bar);
            }

            cleanup_3();
        }

        cleanup_2();
    }

    cleanup_1();

    return return_value;
}

But then this looks like the Pyramid of Doom

 

Torvalds stresses readability over things like "not using gotos because blah blah blah". In this case, it's much easier to read and understand the function with the gotos than the if-statement pyramid and they perform exactly the same function.

 

EDIT: I would like to stress this is a very specific use-case. If you start seeing this pattern but you can return or break early without issues, then you shouldn't use goto.

Perhaps case/switch would be able to make it look neat while still avoiding gotos? (that said I don't use case/switch often enough to know how to use it well enough to have a nice and elegant solution of the top of my head)

 

17 hours ago, Nuluvius said:

It depends. Preconditions and Guard Clauses are the place for these (break, continue, exit/return). This reduces nesting thus improving Cyclomatic Complexity and readability.

Sure there are cases where a break/continue/exit/return can fit better than a condition within the while statement but it's something that an experienced programmer will know when and where they can use while a beginner might go overboard and end up with hard to maintain spaghetti code. Also I realize I made a typo and accidentally said not rather than "note".

Link to comment
Share on other sites

Link to post
Share on other sites

On 11/28/2017 at 6:47 PM, ElfFriend said:

That said when I was learning Python (my first language) I didn't know enough to efficiently control code flow so I considered gotos to be the best option. Then I realized functions and while/for loops are a thing and the need for a goto faded away. 

I didn't even know python had a goto equivalent (Unless you are referring to this April fools module...)

Edited by Guest
April's -> April
Link to comment
Share on other sites

Link to post
Share on other sites

1 hour ago, tjcater said:

I didn't even know python had a goto equivalent (Unless you are referring to this April's fools module...)

I considered using a goto. Which basically means I was googling how to do gotos in Python without actually being successful at it... I guess that further strengthens the "gotos are intuitive" idea if I was able to basically come up with the concept on my own. Thankfully I messaged a more experienced programmer asking how to use gotos in Python and he pretty quickly explained to me why gotos shouldn't be used and what I should be doing instead. 

Link to comment
Share on other sites

Link to post
Share on other sites

14 hours ago, ElfFriend said:

Perhaps case/switch would be able to make it look neat while still avoiding gotos? (that said I don't use case/switch often enough to know how to use it well enough to have a nice and elegant solution of the top of my head)

A switch/case wouldn't work here for the following reasons:

  • Switch/case iterates once. So you'd need a loop to iterate through the steps
  • In each step, you still need to verify that the action was successful and don't do the remaining steps if it wasn't
    • This along with a loop means the loop has two conditions needed to break out of: if you're done or if there was a problem
    • And if you need to add or subtract a step, you have to modify more places which can be prone to human errors (like forgetting to update one part)
  • And you still need to clean up based on what step you managed to get through.

So the best I could come up with using a switch/case is:

const int MAX_STEPS = 4; // This needs to be updated if steps are added or removed

int foo(int bar)
{
    int return_value = 0;
    int step_num = 0;
    int action_ok = 1;
    
    while(step_num != MAX_STEPS && action_ok == 1){
      	// If you add or remove a step between one of these steps, 
        // you have to update every thing else down the chain
        switch(step_num){
        case 0:
            allocate_resources_1();
            action_ok = do_something(bar);
            break; 
        case 1:
            allocate_resources_2();
            action_ok = init_stuff(bar);
            break;
        case 2:
            allocate_resources_3();
            action_ok = prepare_stuff(bar);
            break;
        case MAX_STEPS - 1:
            return_value = do_the_thing(bar);
            break;
        }
        step_num ++;
    }

    // The numbers here also have to be updated if steps are added or removed
    if (step_num > 1)
        cleanup_3();
    if (step_num > 0)
        cleanup_2();
    cleanup_1();
    return return_value;
}

The reason why Linus accepts gotos for the Linux kernel is he's trying to minimize the following:

  • Fatigue in reading the code. The example I provided in this post is still rather complicated
  • Minimizing the complexity, because the more complex it is to fix something, the more likely someone will forget to do something and introduce a bug.
    • Complexity is relatively simple to assess: how many branches do you have?

EDIT: As a curiosity check, I found that goto only works in the function that the labels are defined in. This is a rule in the C standard, so there's no real danger of software wide spaghetti code where there are jumps to various parts of the application. It's limited only to the function.

Link to comment
Share on other sites

Link to post
Share on other sites

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

×