Programming Pariahs – goto Statements

xkcd: gotoSource: http://xkcd.com/292/

Interestingly, although I do not believe I’ve ever been formally taught how goto works, I have been told on numerous occasions in classes that it’s something to avoid while programming. In fact, during one of my introductory programming courses, I distinctly remember the TA scoffing at my usage of goto in a C# program (where it was actually required). I’ll walk through the rationale behind the advice after a quick explanation of how goto is used in programming.

So, how does this newfangled goto thing work?

goto is used to jump to another location in a program. Upon reaching a goto statement, your program will jump to wherever the goto commands it to.

Though the details of goto differ between programming languages, here’s an example in C++ to print “I hate using loops!” five times:

#include <stdio.h>

int main()
{
int i = 0;

this_is_a_label:
   printf("I hate using loops!\n");
   i = i + 1;
   if (i < 5)
      goto this_is_a_label; // jump to the label if we're not done

   return 0;
}

This code has the following output:

I hate using loops!
I hate using loops!
I hate using loops!
I hate using loops!
I hate using loops!

Notice the label on line 7, “this_is_a_label:“. Labels are used as locations that you can jump to with a goto statement. The statement “goto this_is_a_label;” tells the program to jump to that label and continue execution from there.

Alright, now why “shouldn’t” I use goto?

Jumping around in code can make it very hard to process mentally while reading it, especially once the code becomes more complex than just one loop. For example, try figuring out what this program does:

#include <stdio.h>

int main()
{
    // Using nested goto loops
    int x = 1;
    outer_loop_begin:
        int y = 1;

        inner_loop_begin:
            printf("(%i,%i)\n", x, y);
            ++y;
            if(y < 4) goto inner_loop_begin;
        // inner loop done

        ++x;
        if (x < 4) goto outer_loop_begin;
    // outer loop done

    return 0;
}

It’s certainly less intuitive than this example using for loops:

#include <stdio.h>

int main()
{
    // Using nested for loops
    for(int x = 1; x < 4; ++x)
    {
        for (int y = 1; y < 4; ++y)
        {
            printf("(%i,%i)\n", x, y);
        }
    }

    return 0;
}

Both of the above examples have the same output:

(1,1)
(1,2)
(1,3)
(2,1)
(2,2)
(2,3)
(3,1)
(3,2)
(3,3)

As I’m sure you could imagine, improper/abusive use of goto can make a program significantly harder both to understand and to debug. You can use goto in a number of clever ways, but most places where you could be using goto you should generally be using some other part of the language (a loop structure, a function call, etc.).

When should I use goto?

The summary version is: whenever you are required to, or whenever it makes your code clearer. The latter is very subjective, so I won’t touch too much on that; however, it’s important to know when you need to use a goto statement.

Exiting from Nested Loops (C++, other languages)

Say hypothetically you’re inside nested loops, and you want to exit the entire structure, not just the loop you are in. Traditionally, a break statement is your trusty tool for exiting a loop, but that will only get you to the outer loop. For example:

#include <iostream>

int main()
{
    int searchValue = 2;

    // Search for a certain j value. Once it's found, exit the search loops
    // and exit the program.
    for (int i = 0; i < 5; ++i)
    {
        for (int j = 0; j < 5; ++j)
        {
            if (j == searchValue)
            {
                std::cout << "Found value " << searchValue
                          << " at (i,j): (" << i << "," << j << ")"
                          << std::endl;
                break; // exit our loops and print the completion message
                // BUG: breaks to the outer i loop
            }
        }
    }

    std::cout << "Search completed!" << std::endl;

    return 0;
}

You were hoping for the following output:

Found value 2 at (i,j): (0,2)
Search completed!

But you ended up with this!

Found value 2 at (i,j): (0,2)
Found value 2 at (i,j): (1,2)
Found value 2 at (i,j): (2,2)
Found value 2 at (i,j): (3,2)
Found value 2 at (i,j): (4,2)
Search completed!

The break statement at line 18 is only exiting your inner for loop. Once that break is hit, i gets increased by one and the loops continue going! To get the desired result, a goto statement will do the trick:

#include <iostream>

int main()
{
    int searchValue = 2;

    // Search for a certain j value. Once it's found, exit the search loops
    // and exit the program.
    for (int i = 0; i < 5; ++i)
    {
        for (int j = 0; j < 5; ++j)
        {
            if (j == searchValue)
            {
                std::cout << "Found value " << searchValue
                          << " at (i,j): (" << i << "," << j << ")"
                          << std::endl;
                goto search_done; // exit our loops and print the completion message
            }
        }
    }
search_done:

    std::cout << "Search completed!" << std::endl;

    return 0;
}

An alternative would be wrapping the loops in a function and returning where the goto is, but depending on where your loop is being called and what work is being done inside of it, goto may be the better choice.

“Falling through” non-empty cases in a switch statement (C#)

Traditionally, if you want multiple cases to do the same thing in a switch statement, your code will look something like this:

switch(direction)
{
    case WEST:
        goWest();
        break;
    case NORTH:
    case SOUTH:
    case EAST:
        System.out.println("There is no exit in that direction");
        break;
    default:
        // Invalid argument
        throw new InvalidArgException("Ya dun goofed!"); // they dun goofed.
        break;
}

This works fine in C#, unless you want to do something in a case before falling through it. In many language, simply omitting the “break;” statement at the end of the case will allow this functionality; however, this is not the case in C#: “A jump statement such as a break is required after each case block, including the last block whether it is a case statement or a default statement. With one exception, (unlike the C++ switch statement), C# does not support an implicit fall through from one case label to another. The one exception is if a case statement has no code.” (MSDN source). This is a good thing in that you won’t have an obscure bug caused by forgetting to put in a break statement; however, it requires using a different technique to fall through your cases.

If you want to “fall through” a non-empty case, you must explicitly state which case to go to:

switch(direction)
{
    case WEST:
        goWest();
        break;
    case NORTH:
        logSomethingHere();
        goto case EAST;
    case SOUTH:
    case EAST:
        System.out.println("There is no exit in that direction");
        break;
    default:
        // Invalid argument
        throw new InvalidArgException("Ya dun goofed!"); // they dun goofed.
        break;
}

Notice the “goto case” at line 8.

Other Uses

There are assuredly other reasons to use goto out there, especially because each programming language is different; however, two examples is enough for this particular article. Check the documentation for your programming language of choice!

References / Recommended further reading

The goto Statement – MSDN

goto (C# Reference) – MSDN

Wikipedia’s article on Goto, especially the Criticism and Decline section.

Advertisements

3 thoughts on “Programming Pariahs – goto Statements

  1. Sean W

    Even in your examples of when you SHOULD use a goto, there are still ways around it. For instance, in your nested loop, I would, personally, wrap that up in a separate function and use exit. If this isn’t possible, the slightly less-clear

    loop 1{
    loop 2{
    dostuff();
    if (stuffhasbeendone) {flag == true; break;]
    } //loop 2

    if (flag = true) {break;}
    } //loop 1

    Pardon my pseudocode. That method is messy, but works.

    I think everyone needs to learn how to use gotos…but also understand the dangers, especially in complicated code. The problems can be mitigated, if you are careful. People also need to understand that, in a lot of corporate programming environments, using a goto is invitation to be tasered in the face.

    Reply
    1. Zach Hoefler Post author

      Very good points. I mentioned the possibility of using a function underneath the goto example, though it doesn’t jump out and it really should’ve been elaborated on more in retrospect.

      My intent was moreso to suggest that it’s usable as a way to exit a complex structure of loops/conditionals/what have you. The example I came up with probably wasn’t the best – that one should almost certainly be implemented as a function as-is – but I couldn’t come up with an example that wasn’t incredibly convoluted or confusing. Which I suppose is really the point; goto is something so very situational that most people are probably better off just avoiding it entirely anyway.

      Reply
  2. Sean W

    Yeah…avoid them, but have them as a tool for that weird time when it makes the most sense. Or, when you are about to retire and really want to piss off the guys that will be maintaining your code afterwards.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s