The linked image has formatting as intended originally. These are exposing so many different ways things look wrong without an official code markup tag.The programming world at large has cast
goto statements as always bad, but attempting to protect bad programmers from themselves is a cause so lost that it has six seasons and a 12-minute epilogue. Seeing someone write messy code and taking away everyone's
goto is like seeing a nigger waving a handgun around and taking away everyone's handgun, failing to identify the problem either way. We don't need common sense
goto control.
All programming constructs and mechanisms can be abused, such that singling out
goto is pointless. Using
goto can be perfectly appropriate and perfectly elegant. C/C++ has at least one case where
goto appears absent a better mechanism, but I'll also show some situations where it's as well to use it.
Suppose we iterate pairs of integers
i then
j, but sometimes want to skip to the next
i, ignoring any remaining
j and any operations after the
j-loop. One way to do this by jumping out and over said operations by placing a label at the end of the
i-loop:
bool avoid(int i, int j);
void process(int i, int j);
void conclude(int i);
for (i=0; i<10; i++)
{
for (j=0; j<10; j++)
{
if (avoid(i, j)) goto next_i;
process(i, j);
}
conclude(i);
next_i: ;
}
This is for lack of labelled
break and
continue, which has been suggested many times and looks like it could actually be in the next C++ release. Until then, the official C solution is
goto, and it is perfectly clear and performant. It is not better to screw around with a flag when you can just jump the flow. Making a separate inner function and returning early can be appropriate though, if it is not too arduous to pass in any local variables needed.
For a different scenario where
goto is perfect, suppose we have a system state update function which must consume all the time given to it. Action functions modify the remaining time and return true if they concluded early, potentially having changed the state:
int state;
bool action_1(int✱ time_ms);
bool action_2(int✱ time_ms);
void action_idle();
void update(int time_ms)
{
update_again:
if (state ꞊꞊ 1)
{
if (action_1(&time_ms)) goto update_again;
}
else if (state ꞊꞊ 2)
{
if (action_2(&time_ms)) goto update_again;
}
else action_idle();
}
The
goto-avoiding way could be a
while loop and incurring an extra test whether any time remains. Probably worse than that is calling itself recursively just for re-entry with a lower amount of time. If the logic is in the position of knowing it needs to consume remaining time, just jump to the top!
Also in the context of state systems, there is nothing wrong with having multiple possible conclusions at the end of a function and a way to jump to the alternative flow, which becomes useful when multiple logic branches use it. Loosely:
{
// Function body with various decisions
// ...
return normal_result;
alt_conclusion:
// Alternative computation
// ...
return alt_result;
}
As with any mechanism, sensible use of it and sensibly-named labels are what makes it pass the readability test. Naming a label "hell" is also funny, everyone should get to do that at least once.
[ + ] AugustineOfHippo2
[ - ] AugustineOfHippo2 1 point 3 weeksApr 2, 2025 19:12:23 ago (+1/-0)
Your second example is a great way to start an infinite loop, or worse, a loop that sometimes takes a long time to complete, causing headaches and difficulties in debugging.
I’m not against goto, and use them in assembly sometimes, but they should be avoided as much as possible.
[ + ] SithEmpire
[ - ] SithEmpire [op] 0 points 3 weeksApr 2, 2025 19:16:26 ago (+0/-0)
[ + ] AugustineOfHippo2
[ - ] AugustineOfHippo2 0 points 3 weeksApr 2, 2025 21:49:28 ago (+0/-0)
[ + ] dukey
[ - ] dukey 1 point 3 weeksApr 2, 2025 18:23:13 ago (+1/-0)
[ + ] SithEmpire
[ - ] SithEmpire [op] 0 points 3 weeksApr 2, 2025 19:12:50 ago (+0/-0)