This article is a response to all my university professors who, for some
reason, think goto
is useless and should be avoided at all costs.
Some use cases
The most important use case there is for goto
is by far error handling when
there are more than 1 points of failure. In this case, you might want to
cleanup some resources while also skipping part of the code that should not be
executed, without having to deal with flags, helper functions, and other
methods that would make the code ugly, slower and error prone. Try rewriting
the following snippet without a goto
:
int
foo(int *bar, int *baz)
{
if (!func1())
goto fail;
if (!func2())
goto fail;
if (!func3())
goto fail;
return 0;
fail:
warn("foo failed");
if (bar != NULL)
free(bar);
if (baz != NULL)
free(baz);
return -1
}
Another use case is breaking out of deeply nested code. Let’s say you’ve got 3
for
loops and there’s a special case in which you really want to break out of
all the loops at once. how do you do that? There are multiple ways you can go
about doing so but one way would be to set a flag and check it on every nested
level.
flag = 0;
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
for (k = 0; k < 10; k++) {
...
if (flag)
break;
}
if (flag)
break;
}
if (flag)
break;
}
Another ugly hack you can use is something another colleague from university showed me, and something I would never use; when the flag is set, manually max out all the loop counters.
A pretty straight-forward solution would also be to put the loop into a
function and use a return
statement to break out of all the loops. That’s
actually a good solution, and I’m aware of it, but I want to provide another
solution, which is also quite faster than using a function since it avoids that
additional function call.
An alternative, and in my opinion, better way of solving this problem would be
by using a (don’t say it, don’t say it) goto
:
flag = 0;
for (i = 0; i < 10; i++) {
for (j = 0; j < 10; j++) {
for (k = 0; k < 10; k++) {
...
if (flag)
goto end;
}
}
}
end:
...
Who cares, anyway?
In the first use case, the code is much more readable and you avoid code
duplication. In the second use case the goto
solution actually does improve
performance. The reason why is simple; we check for flag
on every single
loop, which means, that in case flag
is never set, we’ll have done 10 * 10 *
10 = 1000 checks just to see if flag
is set. And that’s just with 3 for
loops going from 0 to 10 each; think how easily this can scale up if you just
increase the iterations. The goto
solution does only one check in the third
loop, which means that, in the above scenario, where flag
never gets set,
we’ll have done only 10 checks - that’s 100 times faster than the other
solution.
Using a function is almost just as fast as using a goto
without a function,
but not having to call a function is generally faster. Both solutions are great
and totally valid, I just want to show an alternative one.
Final note
goto
does have its place but it should be used carefuly; if you overuse it,
your code will either become incomprehensible, or flat out broken. The use
cases I showcased in this post are very common and sometimes the code can be
vastly improved with just a simple goto
if used correctly.
Again, thanks to both my colleagues who helped me improve this article with their recommendations.