Probably coming too late to the discussion, but commenting anyway... The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. Other than simple integer FOR loops, basically all control-flow in the FORTRAN of those days was accomplished with GOTO -- and to numbered lines, not labels. Things we take for granted in all languages today like { code blocks }…
Can you give an example of where you've used goto in C or a C-like language? Or a rough estimate of the number of times you've found goto to be the best solution? I agree with your post in principle, but in practice I can't think of a single example where I've used a goto that wasn't eventually refactored to something better that didn't have the goto. Edit: Probably the most common example (and one given early in the…
And I would too... but sometimes there are too many local variables involved to do that cleanly. And, ultimately, if you are making your code less clean to avoid using a goto then you avoided unwisely.
> Can you give an example of where you've used goto in C or a C-like language?
Well there have been several fairly normal examples cited by others. Let me set out my stall and present a more controversial one.
Suppose I have a switch() block (which is sorta a glorified goto already!) and I have two cases that have the same epilogue. For example:
switch (get_next_thing_to_do()) {
case ACTION_A:
do_action_a();
goto common_post_action_cleanup;
case ACTION_B:
do_action_b();
common_post_action_cleanup:
cleanup_something();
z = nullptr;
x = x->next;
// yadda yadda
break;
case ACTION_C:
do_action_c();
// NOTE: we don't need to do the "cleanup" here
break;
}
Now the first thing you should try is to put all of the shared code in its own function. Again, this isn't always practical if what you need to do effects a lot of local variables. It also means that those "cleanup" steps are now implemented far away from the rest of the logic which might hurt comprehensibility on its own.Another option is to just suck it up, repeat the code twice, and then just trust that the compiler will clean it up. First, that won't be as attractive if you have to do it 10 times instead of twice. Second, you're now violating the DRY principal which to me is sacrosanct. Experience has taught me that if someone updates the cleanup code for ACTION_A they will forget to update ACTION_B at the same time. If you care that the two cases always end with the same epilogue you need to have a single copy of the code.
You can try to get back into compliance with DRY by using something like a C macro, but now you're not winning the war against ugly code.
Finally you could just put the cleanup code after the switch(), either by retesting the enum value (requiring it to be stored in a temporary variable) or by adding some other new cleanup_needed boolean flag. But now we're adding control flow that wasn't there before. I personally believe that doesn't help readability or maintainability. You're now just adding more variables to understand and probably getting worse generated assembly to boot.
While many programmers will look at this and feel vaguely icky about the presence of a "goto" if you really stop and look at the block of code it's actually quite readable...at least as long as the label and the goto are near each other. It is immediately apparent that two "states" are sharing some code and why.
This little example is obviously made up. However I've spent a lot of my career writing things like hand-crafted state machines and situations exactly like this (where two "states" are different but share the same epilogue) comes up all the time. State machine code like this is famously hard to read but often sharing code via goto like this is the least-bad option for expressing non-hierarchical control flow.