goto jump

Pages: 12
Hello fellows. I remember my first attempts to code something on a personal computer - my old ZX-81. I remember some syntax which allowed me to execute parts of code jumping on another place - like this one goto. I guess that everyone remembers the first program printing continually a simple sentence on screen using an infinite loop goto :)
Is there a reason today to avoid this goto in order to jump in the code?
Do you use it anyway?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{
  std::string input;
  
  while (1)
  {
    std::cin >> input;
    std::cout << "Input text : " << input << std::endl;

    if (input == "jump")
        goto oldiesButGoodies;
  }

oldiesButGoodies:
  std::cout << "Bye!" << std::endl;
  return 0;
}
Last edited on
There's almost no reason with today's structured programming to use goto. Use of goto led to 'spaghetti' code which was a nightmare to maintain/amend/debug/understand etc.

Without goto, this can be simply coded as:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main() {
	std::string input;

	do {
		std::cin >> input;
		std::cout << "Input text : " << input << '\n';
	} while (input != "jump");

	std::cout << "Bye!" << std::endl;
}


or as a 1-liner:

1
2
3
4
5
6
7
include <iostream>

int main() {
	for (std::string input; (std::cin >> input) && (std::cout << "Input text : " << input << '\n') && (input != "jump"); );

	std::cout << "Bye!" << std::endl;
}


I started coding many, many, many moons ago using HP TSB (Time-Shared Basic). This was a variation of Dartmouth Basic. Every line had a line number and all if-statements referenced a line number for the true condition, the same with subroutines etc. If you produced a flow-chart of the program first, then the flow-chart could then be coded into Basic. If you then needed to amend the program though, it was a nightmare. Fixing bugs etc could (and often did) introduce other bugs (and sometimes more bugs than were supposed to fix!). Reading and understanding the code was another nightmare and often no sense of program 'structure' could be easily obtained just be 'looking at' the code - as the code jumped all over the place with line number references.

Don't!
Last edited on
Yes. goto code quickly becomes unpossible to follow. goto (or similar) is used in assembly language or really old/simple/low level languages like original basic when the concept of a subroutine was not available, and for that, you have to do it. But we have subroutines, so goto serves almost no purpose in clean code. The exceptions are things like jumping out of nested loops, where break is not sufficient and other methods are clunky.

the 'never use goto' thing is mostly tied to abuse, though. Its an elegant solution in some cases, and can be very powerful, but one to many rookies have made one too many messes, all of which can be avoided by using another approach, for it to survive the hatred most coders have of it. I would use it, but, its not allowed at most workplaces or on most projects.
1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main()
{
  std::string input;
  while( std::cout << "? " && std::cin >> input && input != "jump" )
      std::cout << "Input text : " << input << '\n' ;

  std::cout << "Bye!\n" ;
}


CoreGuidelines:

ES.76: Avoid goto
Reason Readability, avoidance of errors. There are better control structures for humans; goto is for machine generated code.
...
Exception Breaking out of a nested loop. In that case, always jump forwards.
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-goto


In general, try to minimise the use of jump statements (except return). So:
ES.77: Minimize the use of break and continue in loops
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-continue
ES.77: Minimize the use of break and continue in loops
Last edited on
https://stackoverflow.com/questions/3517726/what-is-wrong-with-using-goto
In general goto has been superseded by more advanced methods that help to maintain a program/code's logical structure. Go to served too many purposes; if I use someone else's goto is it a function, is it a loop, am I going to return from it or is it going to take us on a goto adventure and never return? <- a function that never returns is generally considered broken.

I loved goto when I first started programming. It's fun to use, and seems easy to understand. The problem with goto is that you need to have a full picture of the code in your head in order to use them, or you have to be disciplined with how you implement them. After a couple hundred lines of code things tend to become messy. In 5,000 lines name-clashes start to be a problem. in 30,000 lines of code a black hole opens up in the center of the hard drive.

If you're making something for yourself and enjoy goto, then use it. However, when you're working with other coders and handing them your code's documentation, it best not be a list of go tos.
Last edited on
Interesting. Thanks.
It's a little bit embarrassing for me, but I know that we could code a better alternative using a do/while(condition) loop. It was not the question.
I thought that goto could be an elegant way to escape a loop, but it's true - it is not really efficient in C++ - more common to ASM with large sequences. Thank you for the explanations ++
Last edited on
Using the same 'structure' as the original - but without goto, then:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

int main() {
	std::string input;

	while (true) {
		std::cin >> input;
		std::cout << "Input text : " << input << '\n';

		if (input == "jump")
			break;
	}

	std::cout << "Bye!" << std::endl;
	return 0;
}


A link to Dijkstra's ground-breaking paper on the case against published in 1968:
https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF

I thought that goto could be an elegant way to escape a loop


It's all to do with code readability/maintainability etc etc. Yes, in some cases goto can be an elegant solution - but this is the exception that proves the 'don't use' rule! eg exiting from multi-level for-loops etc - which would also be more efficient than the alternative of using an exit flag tested in the for-loops conditions.
Last edited on
> I thought that goto could be an elegant way to escape a loop

If goto turns out to be more elegant, use it; blithely ignore the pedants.


> it is not really efficient in C++

It is a question of readability and maintainability: of programmer efficiency, rather than machine efficiency.
it is not really efficient in C++

I don't know where you picked up that tidbit, but it is wrong.
All you have to do to verify this is to turn on the generated assembly listing. You will see that goto generates (among other instructions) a jump instruction to the specified label. It is no more or less efficient than break or continue. For example, if you're exiting a loop early, all three instructions have to unwind (i.e. call destructors for) anything that was pushed on the stack in the loop and then adjust the stack frame. That does not mean that you should use goto for all the reasons already given.

As a programming manager, I would not tolerate any programmers that used goto in their code without special dispensation to do so.

As a compiler author, I would use goto in the generated intermediate code. The generated code is of course meant to be efficient, not necessarily readable by the casual reader.
The efficiency issue is probably highlighting misuse. A jump that isnt necessary (a loop, a condition, a function call, etc require one) is a performance hit. The jump itself is not a problem. Its probably being stupidly picky anyway: you can write unnecessary loops or conditions and who knows what else inefficiently too. I can't think of well written code using a goto that would be slower than using something else: in fact, it has a chance of being faster.

Maybe think of it exactly like inline assembly. Its powerful, but its hard to read, hard to write, hard to debug or modify/improve/enhance. It has no place in most code for those reasons, even if its a potent low level tool, its best to generally avoid low level constructs without good cause, and even with good cause, going no lower than C level is often the best middle ground
Last edited on
Is there a reason today to avoid this goto in order to jump in the code?

Invert and 'massage' the question:

"Is there a reason to use goto in modern C++ that isn't 'easier to code and understand' using higher level C++ language alternatives?"

There are reasons, but they are few and far between. Better to avoid using goto if at all possible. Leverage the tools the C++ stdlib gives you.
Geckoo wrote:
Is there a reason today to avoid this goto in order to jump in the code?
goto is a burden for the future. When you have just a few lines of code it is not a problem. The more code you have the harder it becomes to follow unusual program flow.

Do you use it anyway?
No. But other similar things like break. I try to avoid skipping lines. Only when breaking a loop is easier than in the header of the loop.
I used it in C, it's a good pattern for doing all your checks at the top and aborting on error.

Here's a similar example: https://github.com/freebsd/freebsd-src/blob/main/usr.bin/passwd/passwd.c#L163

With C++ and its destructors ... not a good idea.

The one thing I only ever used once (I'm embarrassed to say) was setjump/longjmp, programming a dongle on DOS.
Last edited on

If goto turns out to be more elegant, use it; blithely ignore the pedants.



As a programming manager, I would not tolerate any programmers that used goto in their code without special dispensation to do so.
As a compiler author, I would use goto in the generated intermediate code. The generated code is of course meant to be efficient, not necessarily readable by the casual reader.


Interesting conversation. I notice that consequently this is a controversial issue to debate. Some explain that it is not a good way because of code readability - others explain that we have to avoid it according to better alternatives. Some said Why Not ? I agree. I guess that all depends on the application which the dev is coding. More you have code - more you need maintainability... However I understand what you mean by readability and high level coding. Thank you for your help.

For the beginners, a simple example which shows the dilemma :)

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <string.h>
#include <vector>
// here all the words available as a dictionary
std::vector<std::string> dictionary = {
    "underground", "crocodile", "computer", "keyboard", 
    "shuttle", "asteroid", "dictionary", "kaleidoscope"
    // and more more more
};

int main()
{
    int letters, index, attempts = 0;
    std::string secretWord; // the word which you have to find
    bool bWinner = false;

    std::cout << "~~~ Guess The Secret Word Game ~~~" << std::endl;

    letters = dictionary.size(); // max index
    srand(time(NULL));
    index = rand() % letters; // randomize index
    // this is our word and its length
    secretWord = dictionary[index];

    std::cout << "The secret word has been chosen : " << std::endl;
    // display for each letter a star
    for (int i = 0; i < secretWord.length(); i++)
        std::cout << "*";

    std::cout << std::endl;
    std::cout << "Give a first suggestion please : " << std::endl;

    do
    { 
        attempts++;
        std::string choice;
        std::cin >> choice;
        // you found the secret word!
        if (secretWord == choice)
        {
            bWinner = true;
            break;
        }
        // give only the right letter(s)
        for (int i = 0; i < secretWord.length(); i++)
        {
            if (i < secretWord.length() && i < choice.length())
            {
                if (secretWord[i] == choice[i])
                    std::cout << choice[i];
                else
                    std::cout << "*";
            }
            else // because your attempt has not the same length maybe
                std::cout << "*";
        }
        std::cout << std::endl;
    } while (!bWinner); // loop

    std::cout << "Congrats! You have guessed the secret word -> " 
              << secretWord << " with " << attempts << " attempts." << std::endl;

    return 0;
}


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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <string.h>
#include <vector>
// here all the words available as a dictionary
std::vector<std::string> dictionary = {
    "underground", "crocodile", "computer", "keyboard", 
    "shuttle", "asteroid", "dictionary", "kaleidoscope"
    // and more more more
};

int main()
{
    int letters, index, attempts = 0;
    std::string secretWord; // the word which you have to find
    std::cout << "~~~ Guess The Secret Word Game ~~~" << std::endl;

    letters = dictionary.size(); // max index
    srand(time(NULL));
    index = rand() % letters; // randomize index
    // this is our word and its length
    secretWord = dictionary[index];

    std::cout << "The secret word has been chosen : " << std::endl;
    // display for each letter a star
    for (int i = 0; i < secretWord.length(); i++)
        std::cout << "*";

    std::cout << std::endl;
    std::cout << "Give a first suggestion please : " << std::endl;

    while(1) { 
        attempts++;
        std::string choice;
        std::cin >> choice;
        // you found the secret word!
        if (secretWord == choice)
            goto winner;
        // give only the right letter(s)
        for (int i = 0; i < secretWord.length(); i++)
        {
            if (i < secretWord.length() && i < choice.length())
            {
                if (secretWord[i] == choice[i])
                    std::cout << choice[i];
                else
                    std::cout << "*";
            }
            else // because your attempt has not the same length maybe
                std::cout << "*";
        }
        std::cout << std::endl;
    }

winner:
    std::cout << "Congrats! You have guessed the secret word -> " 
              << secretWord << " with " << attempts << " attempts." << std::endl;

    return 0;
}
Last edited on
Yes. This one is becoming less readable :/

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <string.h>
#include <vector>
// here all the words available as a dictionary
std::vector<std::string> dictionary = {
    "underground", "crocodile", "computer", "keyboard", 
    "shuttle", "asteroid", "dictionary", "kaleidoscope"
    // and more more more
};

int main()
{
    int letters, index, attempts = 0;
    std::string secretWord; // the word which you have to find
    std::cout << "~~~ Guess The Secret Word Game ~~~" << std::endl;

    letters = dictionary.size(); // max index
    srand(time(NULL));
    index = rand() % letters; // randomize index
    // this is our word and its length
    secretWord = dictionary[index];

    std::cout << "The secret word has been chosen : " << std::endl;
    // display for each letter a star
    for (int i = 0; i < secretWord.length(); i++)
        std::cout << "*";

    std::cout << std::endl;
    std::cout << "Give a first suggestion please : " << std::endl;
loop:
    attempts++;
    std::string choice;
    std::cin >> choice;
    // you found the secret word!
    if (secretWord == choice)
        goto winner;
    // give only the right letter(s)
    for (int i = 0; i < secretWord.length(); i++)
    {
        if (i < secretWord.length() && i < choice.length())
        {
            if (secretWord[i] == choice[i])
                std::cout << choice[i];
            else
                std::cout << "*";
        }
        else // because your attempt has not the same length maybe
            std::cout << "*";
    }
    std::cout << std::endl;
    goto loop;
winner:
    std::cout << "Congrats! You have guessed the secret word -> " 
              << secretWord << " with " << attempts << " attempts." << std::endl;

    return 0;
}
Last edited on
Slightly rewritten:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <iomanip>

const std::vector<std::string> dictionary {
	"underground", "crocodile", "computer", "keyboard",
	"shuttle", "asteroid", "dictionary", "kaleidoscope"
	// and more more more
};

int main() {
	std::mt19937 rng(std::random_device {}());
	std::uniform_int_distribution<size_t> distrib(1, dictionary.size());
	const size_t maxAttmpt { 10 };
	size_t attempts {};
	bool bWinner {};
	const auto secretWord { dictionary[distrib(rng) - 1] };

	std::cout << "~~~ Guess The Secret Word Game ~~~\n";
	std::cout << "The secret word has been chosen :\n";
	std::cout << std::setfill('*') << std::setw(secretWord.size()) << '*' << '\n';
	std::cout << "You have " << maxAttmpt << " attempts\n";

	do {
		std::string choice;

		std::cout << "What is your " << ++attempts << " attempt: ";
		std::cin >> choice;

		if (secretWord == choice)
			bWinner = true;
		else {
			for (size_t i {}; i < secretWord.length(); ++i)
				std::cout << (i < choice.length() ? (secretWord[i] == choice[i] ? choice[i] : '*') : '*');

			std::cout << '\n';
		}
	} while (!bWinner && attempts < maxAttmpt);

	if (bWinner)
		std::cout << "Congrats! You have guessed the secret word -> "
		<< secretWord << " with " << attempts << " attempts.\n";
	else
		std::cout << "Sorry. The word was " << secretWord << '\n';
}


57 lines of code (with goto) is not a lot of code to plow through, but it is the beginning of when looking at refactoring to shove some of the code in main into functions could start making sense.
The “why not” is not really a reasonably-debatable topic. The reasons for avoiding GOTO are pretty bedrock solid.

The (sociological) problem today began with the highly-opinionated tongue-in-cheek paper “Goto Considered Harmful” by Edgar Dijkstra. (Yes, that Dijkstra, who knows a thing or two more about computers than you do.)

The very basis of the paper is this: Dijkstra observed that code using GOTO was typically less-readable, less-maintainable, and less-correct than code that was better-structured. His paper was an effort to drolly shove people in the direction of using more structure in programming.

This basically kicked off the “structured programming” orgy that indoctrinated sheeple to this day.

GOTO is not evil, but it has its place. If you need it*, use it.

The reality is that bad programmers can write bad code with or without GOTO. They are significantly enabled when they are allowed to use GOTO, though.


In the case of the original code, such a use of GOTO has always been gratuitous. An equivalent, but infinitely less brittle construct is to use a break:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int main()
{
  while (true)
  {
    std::string input;
    getline( std::cin, input );
    std::cout << "Input text: " << input << "\n";
    
    if (input == "jump") break;
  }
  std::cout << "Bye!\n";
}





* Chances are pretty close to 1 that you do not need it.
This, of course, is how it should be coded :) :) :)

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <iomanip>

const std::vector<std::string> dictionary {
	"underground", "crocodile", "computer", "keyboard",
	"shuttle", "asteroid", "dictionary", "kaleidoscope"
	// and more more more
};

int main() {
	std::mt19937 rng(std::random_device {}());
	std::uniform_int_distribution<size_t> distrib(1, dictionary.size());
	const size_t maxAttmpt { 10 };
	size_t attempts {};
	bool bWinner {};
	const auto secretWord { dictionary[distrib(rng) - 1] };

	std::cout << "~~~ Guess The Secret Word Game ~~~\n";
	std::cout << "The secret word has been chosen :\n";
	std::cout << std::setfill('*') << std::setw(secretWord.size()) << '*' << '\n';
	std::cout << "You have " << maxAttmpt << " attempts\n";

L1:
	std::string choice;
	size_t i {};

	std::cout << "What is your " << ++attempts << " attempt: ";
	std::cin >> choice;

	if (secretWord == choice) goto L6;
L3:
	if (i >= secretWord.length()) goto L7;
	std::cout << (i < choice.length() ? (secretWord[i] == choice[i] ? choice[i] : '*') : '*');
	++i;
	goto L3;
L6:
	bWinner = true;
	goto L2;
L7:
	std::cout << '\n';
L2:
	if (!bWinner && attempts < maxAttmpt) goto L1;
	if (!bWinner) goto L4;
	std::cout << "Congrats! You have guessed the secret word -> "
		<< secretWord << " with " << attempts << " attempts.\n";
	goto L5;
L4:
	std::cout << "Sorry. The word was " << secretWord << '\n';
L5:
	return 0;
}


Much more readable - with just 7 labels......... :)

Does this work or does it have a subtle bug?
Last edited on
Beautiful.

Whenever you use goto, you have to comment "sue me", "bite me", or similar, next to it :)
"The Pit of Hell, the Inner Loop"
https://youtu.be/FJJTYQYB1JQ?t=2470
Pages: 12