I'd say that it's a fact that using goto
is considered a bad practice when it comes to programming in C/C++.
However, given the following code
for (i = 0; i < N; ++i)
{
for (j = 0; j < N; j++)
{
for (k = 0; k < N; ++k)
{
...
if (condition)
goto out;
...
}
}
}
out:
...
I wonder how to achieve the same behavior efficiently not using goto
. What i mean is that we could do something like checking condition
at the end of every loop, for example, but AFAIK goto will generate just one assembly instruction which will be a jmp
. So this is the most efficient way of doing this I can think of.
Is there any other that is considered a good practice? Am I wrong when I say it is considered a bad practice to use goto? If I am, would this be one of those cases where it's good to use it?
Thank you
Alternative - 1
You can do something like follows:
bool
variable in the beginningisOkay = true
for
loop conditions, add an extra conditionisOkay == true
isOkay = false
.This will make your loops stop. An extra
bool
variable would be sometimes handy though.Alternative - 2
Secondly. if the above loop iterations are in a function, best choice is to
return
result, when ever the custom condition is satisfied.as far as your comment on efficiency compiling the both options in release mode on visual studio 2017 produces the exact same assembly.
and with a flag.
both produce..
so there is no gain. Another option if your using a modern c++ compiler is to wrap it in a lambda.
again this produces the exact same assembly. Personally I think using goto in your example is perfectly acceptable. It is clear what is happening to anyone else, and makes for more concise code. Arguably the lambda is equally as concise.
If you have deeply-nested loops like that and you must break out, I believe that
goto
is the best solution. Some languages (not C) have abreak(N)
statement that will break out of more than one loop. The reason C doesn't have it is that it's even worse than agoto
: you have to count the nested loops to figure out what it does, and it's vulnerable to someone coming along later and adding or removing a level of nesting, without noticing that the break count needs to be adjusted.Yes,
gotos
are generally frowned upon. Using agoto
here is not a good solution; it's merely the least of several evils.In most cases, the reason you have to break out of a deeply-nested loop is because you're searching for something, and you've found it. In that case (and as several other comments and answers have suggested), I prefer to move the nested loop to its own function. In that case, a
return
out of the inner loop accomplishes your task very cleanly.(There are those who say that functions must always return at the end, not from the middle. Those people would say that the easy break-it-out-to-a-function solution is therefore invalid, and they'd force the use of the same awkward break-out-of-the-inner-loop technique(s) even when the search was split off to its own function. Personally, I believe those people are wrong, but your mileage may vary.)
If you insist on not using a
goto
, and if you insist on not using a separate function with an early return, then yes, you can do something like maintaining extra Boolean control variables and testing them redundantly in the control condition of each nested loop, but that's just a nuisance and a mess. (It's one of the greater evils that I was saying using a simplegoto
is lesser than.)In this case you don't wan't to avoid using
goto
.In general the use of
goto
should be avoided, however there are exceptions to this rule, and your case is a good example of one of them.Let's look at the alternatives:
Or:
Neither of these is as concise or as readable as the
goto
version.Using a
goto
is considered acceptable in cases where you're only jumping ahead (not backward) and doing so makes your code more readable and understandable.If on the other hand you use
goto
to jump in both directions, or to jump into a scope which could potentially bypass variable initialization, that would be bad.Here's a bad example of
goto
:A C++ compiler will throw an error here because the
goto
jumps over the initialization ofy
. C compilers however will compile this, and the above code will invoke undefined behavior when attempting to printy
which will be uninitialized if thegoto
occurs.Another example of proper use of
goto
is in error handling:This performs all of the cleanup at the end of the function. Compare this to the alternative:
Where the cleanup is done in multiple places. If you later add more resources that need to be cleaned up, you have to remember to add the cleanup in all of these places plus the cleanup of any resources that were obtained earlier.
The above example is more relevant to C than C++ since in the latter case you can use classes with proper destructors and smart pointers to avoid manual cleanup.
Break your for loops out into functions. It makes things significantly easier to understand because now you can see what each loop is actually doing.