Apropos of What kind of bugs do "goto" statements lead to? Are there any historically significant examples? I am not that learned in C, and to me the puzzle is that a single exit of a function is mandatory, but a goto is considered bad. Often one has to exit a function on a error condition, and then use a goto to the exit point. Also, I am fond of using protothreads. Is that not a form of goto?
Languages are full of gotos. Only thing is, they've been abstracted away to make the use of them safer. A function with a return value is an abstracted goto, a while loop is an abstracted goto, every conditional statement is an abstracted goto.
Just take a look at the CPU stack and you will see lots of jmp instructions, which actually are gotos.
So in short: they're still there but abstracted to make them safer to use.
If we take a look at the protothread we see that the macro is not creating a goto but using a switch-case statement. So protothreads are not goto.
The protothread does create multiple exit points. A single exit point of a function is not mandatory but it's a good rule of thumb that when the function requires multiple exit points or many indentations you can assume that the function you've written is too large. You have to consider both because you can avoid indentation by using many exit points or you can have one exit points by writing complex if-statements, loops and using gotos. Neither of those are good.
The reason why goto is bad is not because of exit points. If we take a look at the
for loop we see that all the jumpers we're talking about is taking out of the programmer's hands. If wanted or not the programmer has no control over these hidden jumpers, it is placed and dynamically indexed for the programmer during compilation. If we take a look at an if-statement, which is a jump ahead instruction, we'll see the same thing. Nowhere in the code is any goto defined by the programmer but created by the language. This is the biggest reason why goto are considered harmful. Gotos are breaking the design of the programming language with the possibility breaking the program while knowing that the programming language is designed to take care of it flawlessly.