19

I have made some research on Stackoverflow about reverse for loops in C++ that use an unsigned integer instead of a signed one. But I still do NOT understand why there is a problem (see Unsigned int reverse iteration with for loops). Why the following code will yield a segmentation fault?

#include <vector>
#include <iostream>
using namespace std;

int main(void)
{
    vector<double> x(10);

    for (unsigned int i = 9; i >= 0; i--)
    {
        cout << "i= " << i << endl;
        x[i] = 1.0;
    }

    cout << "x0= " << x[0] << endl;

    return 0;
}

I understand that the problem is when the index i will be equal to zero, because there is something like an overflow. But I think an unsigned integer is allowed to take the zero value, isn't it? Now if I replace it with a signed integer, there is absolutely no problem.

Does somebody can explain me the mechanism behind that reverse loop with an unsigned integer?

Thank you very much!

Community
  • 1
  • 1
Benjamin
  • 366
  • 1
  • 3
  • 8
  • 5
    `i >= 0` is always true for unsigned `i`, so the loop never terminates. – TonyK Jan 28 '12 at 08:58
  • Read compiler warnings, they are useful. In this case, your compiler should probably have warned you about the fact that the condition in your loop is always true. – dragonroot Jan 28 '12 at 09:20
  • @dragonroot: Unfortunately not. I use the -Wall flag of g++. Do you know a compiler flag that will detect this kind of problem? Thanks. – Benjamin Jan 28 '12 at 09:27
  • @dragonroot: I already have these warnings enabled but the compiler did not tell me anything. – Benjamin Feb 15 '12 at 10:50

5 Answers5

36

The problem here is that an unsigned integer is never negative.

Therefore, the loop-test:

i >= 0

will always be true. Thus you get an infinite loop.

When it drops below zero, it wraps around to the largest value unsigned value.
Thus, you will also be accessing x[i] out-of-bounds.

This is not a problem for signed integers because it will simply go negative and thus fail i >= 0.

Thus, if you want to use unsigned integers, you can try one of the following possibilities:

for (unsigned int i = 9; i-- != 0; )

and

for (unsigned int i = 9; i != -1; i--)

These two were suggested by GManNickG and AndreyT from the comments.


And here's my original 3 versions:

for (unsigned int i = 9; i != (unsigned)0 - 1; i--)

or

for (unsigned int i = 9; i != ~(unsigned)0; i--)

or

for (unsigned int i = 9; i != UINT_MAX; i--)
Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • Or have `i` be *one more* than the index, so that `0` is a proper termination condition. As tricky as the rest though. – Matthieu M. Jan 28 '12 at 13:23
  • Aren't integer overflow and underflow undefined behavior? – josefx Jan 28 '12 at 14:17
  • 2
    @josefx Only signed integer over/underflow is undefined behavior. – Mysticial Jan 28 '12 at 18:19
  • 3
    Or `for (unsigned int i = 9; i-- != 0; )` – GManNickG Aug 28 '12 at 04:09
  • 1
    @GManNickG Ha. I never considered dumping the decrement into the test. :) – Mysticial Aug 28 '12 at 04:31
  • 3
    I'm not sure what the purpose of all those complicated conditions is. You can simply do `for (unsigned int i = 9; i != -1; i--)`. No need for any casts. A direct comparison to `-1` will automatically perform all necessary conversions. `-1` is equivalent to `UINT_MAX` in this context, but `-1` is better because it is type-independent. – AnT stands with Russia Aug 28 '12 at 05:53
  • @AndreyT I wasn't sure on the exact implicit conversion rules. That's why I made the casts explicit. But if you say it works regardless, I can make the edit. – Mysticial Aug 28 '12 at 05:55
  • If you put a `cout << i << ' ';` as the body of each loop, you will see that i for the first loop ranges [9-0] inclusive, while for the rest of the loops, i ranges [10-0] inclusive. – Galaxy Dec 28 '17 at 06:10
  • I would like to propose two of my loops: You don't need a type cast to take the bitwise not of `0`, which is all 1s anyway. For signed int, all 1s is equal to `-1`, and for unsigned int all 1s is equal to `UINT_MAX`. I must point out that this approach works with an integer data type of any size in bytes. No matter what size in bytes the int literal `0` takes up, all bits get flipped to one. `for (unsigned int i = 10; i != ~0; i--)` – Galaxy Dec 28 '17 at 06:11
  • The next one is basically the same thing but compares i to 32 1s, or 8 fs in hexadecimal. This assumes that an unsigned int takes up 4 bytes or 32 bits of space, hence the 8 fs in the hexadecimal number. That might be a problem if int is not 4 bytes in size, which could occur on weird and obscure compilers/operating systems/architectures such as on embedded systems. So one must be careful with such comparisons. `for (unsigned int i = 10; i != 0xffffffff; i--)` – Galaxy Dec 28 '17 at 06:19
  • 2
    The answer from @GManNickG is neat but it needs to start from 10 instead of 9 `for (unsigned int i = 10; i-- != 0; )` – Salem Derisavi May 25 '18 at 21:09
6

The problem is, your loop allows i to be as low as zero and only expects to exit the loop if i is less than 0. Since i is unsigned, it can never be less than 0. It rolls over to 2^32-1. That is greater than the size of your vector and so results in a segfault.

MAK
  • 26,140
  • 11
  • 55
  • 86
4

Whatever the value of unsigned int i it is always true that i >= 0 so your for loop never ends.

In other words, if at some point i is 0 and you decrement it, it still stays non-negative, because it contains then a huge number, probably 4294967295 (that is 232-1).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
3

The problem is here:

for (unsigned int i = 9; i >= 0; i--) 

You are starting with a value of 9 for an unsigned int and your exit definition is i >= 0 and this will be always true. (unsigned int will never be negative!!!). Because of this your loop will start over (endless loop, because i=0 then -1 goes max uint).

Beachwalker
  • 7,685
  • 6
  • 52
  • 94
2

As you said a decrease of an unsigned below zero, which happens right after the last step of the loop, creates an overflow, the number wraps around to its maximum value and thus we end up with an infinite loop.

Does somebody can explain me the mechanism behind that reverse loop with an unsigned integer?

My preferred method for a reverse loop with an index is this:

for (unsigned int i = 9; i > 0; --i) {
    cout << "i= " << x[i - 1] << endl;
}

and that is why because it maps most closely to the normal loop equivalent:

for (unsigned int i = 0; i < 9; ++i) {
    cout << "i= " << x[i] << endl;
}

If then you need to access the indexed element multiple times and you don't want to continuously write [i - 1], you can add something like this as the first line in the loop:

auto& my_element = my_vector[i - 1];
Dimitrios Menounos
  • 545
  • 1
  • 6
  • 17