A 'for' loop that appears to be practically infinite

C++

C++ Problem Overview


I'm debugging some code at the moment, and I've come across this line:

for (std::size_t j = M; j <= M; --j)

(Written by my boss, who's on holiday.)

It looks really odd to me.

What does it do? To me, it looks like an infinite loop.

C++ Solutions


Solution 1 - C++

std::size_t is guaranteed by the C++ standard to be a unsigned type. And if you decrement an unsigned type from 0, the standard guarantees that the result of doing that is the largest value for that type.

That wrapped-around value is always greater than or equal to M1 so the loop terminates.

So j <= M when applied to an unsigned type is a convenient way of saying "run the loop to zero then stop".

Alternatives such as running j one greater than you want, and even using the slide operator for (std::size_t j = M + 1; j --> 0; ){ exist, which are arguably clearer although require more typing. I guess one disadvantage though (other than the bewildering effect it produces on first inspection) is that it doesn't port well to languages with no unsigned types, such as Java.

Note also that the scheme that your boss has picked "borrows" a possible value from the unsigned set: it so happens in this case that M set to std::numeric_limits<std::size_t>::max() will not have the correct behaviour. In fact, in that case, the loop is infinite. (Is that what you're observing?) You ought to insert a comment to that effect in the code, and possibly even assert on that particular condition.


1 Subject to M not being std::numeric_limits<std::size_t>::max().

Solution 2 - C++

What your boss was probably trying to do was to count down from M to zero inclusive, performing some action on each number.

Unfortunately there's an edge case where that will indeed give you an infinite loop, the one where M is the maximum size_t value you can have. And, although it's well defined what an unsigned value will do when you decrement it from zero, I maintain that the code itself is an example of sloppy thinking, especially as there's a perfectly viable solution without the shortcomings of your bosses attempt.

That safer variant (and more readable, in my opinion, while still maintaining a tight scope limit), would be:

{
    std::size_t j = M;
    do {
        doSomethingWith(j);
    } while (j-- != 0);
}

By way of example, see the following code:

#include <iostream>
#include <cstdint>
#include <climits>
int main (void) {
    uint32_t quant = 0;
    unsigned short us = USHRT_MAX;
    std::cout << "Starting at " << us;
    do {
        quant++;
    } while (us-- != 0);
    std::cout << ", we would loop " << quant << " times.\n";
    return 0;
}

This does basically the same thing with an unsigned short and you can see it processes every single value:

Starting at 65535, we would loop 65536 times.

Replacing the do..while loop in the above code with what your boss basically did will result in an infinite loop. Try it and see:

for (unsigned int us2 = us; us2 <= us; --us2) {
    quant++;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMichael BullockView Question on Stackoverflow
Solution 1 - C++BathshebaView Answer on Stackoverflow
Solution 2 - C++paxdiabloView Answer on Stackoverflow