Using goto
, using continue
, using multiple break
, or using multiple return
are all, if abused, different flavours of spaghetti coding, all them could be used to create non-conditional branches. Some examples of abuse:
goto
is considered very bad if used for anything else but a jump downwards, for example to an error handler. (While that may be an acceptable use of goto
, it still starts the whole beating-the-dead-horse goto-is-considered-harmful debate, so I'd avoid goto
for that reason alone.)
continue
non-conditionally jumps upwards, which is considered bad: one definition of spaghetti code is code which jumps non-conditionally both upwards and downwards. It is particularly bad when multiple continue
are added to the same loop. In generally, the existance of continue
is a pretty certain sign of a loop that needs to be rewritten.
Multiple break
and multiple return
can be abused to break out from complex, nested loops, making the code hard to read and maintain. Also, the break
method as demonstrated in the question, enforces the use of the somewhat obscure do-while(false) loop.
Generally, all of these methods are considered bad practice since they can easily be abused. You'll simply have to pick the one which is the least bad: in other words the most readable and least obscure.
I believe that multiple returns
is the most readable form, since it can be mixed in with some function result variable, which you'll possibly want to have anyway:
result_t func ()
{
if (a)
{
doA();
return RESULT_A;
}
... // you'll need lots of if statements here to justify this program design
if (z)
{
doZ();
return RESULT_Z;
}
return RESULT_NORMAL;
}
A side note regarding resource allocation inside the function.
If the above function needs to free some allocated resources and it needs to do so always, you should do that with RAII, so that when the local variable goes out of scope, it cleans up itself.
If it only needs to free some allocated resources in some cases (for example upon error), then you should not do that inside every if statement (unmaintainable), nor should you implement some 1980s BASIC programmer's "on error goto". Instead, consider adding a wrapper function outside the main function, to make the program maintainable and minimize the clutter:
result_t wrapper ()
{
stuff = allocate(); // if needed
result_t result = func(stuff);
if(result == BAD)
{
deallocate(stuff);
}
return result;
}