views:

196

answers:

8

I have a loop that has to go from N to 0 (inclusively). My i variable is of type size_t which is usually unsigned. I am currently using the following code:

for (size_t i = N; i != (size_t) -1; --i) {
    ...
}

Is that correct? Is there a better way to handle the condition?

Thanks,

Vincent.

+4  A: 

You can use this:

for (size_t i = n + 1; i-- > 0;)
{
}

Hope that helps.

Samuel_xL
That will go from `n-1` to 0, not from `n` to 0.
SoapBox
you're right, I missed the "inclusively" requirement, that's fixed, thanks
Samuel_xL
It wouldn't work in the pathological case with `n` being `SIZE_MAX`. Of course, you probably wouldn't want to be iterating from `SIZE_MAX` to 0 anyway...
jamesdlin
A: 

Since unsigned integer will roll into its max value when decremented from zero, you can try the following, provided N is less then that maximum value (someone please correct me if this is UB):

for ( size_t i = N; i <= N; i-- ) { /* ... */ }
Nikolai N Fetissov
This will probably re-load the value of N on each iteration, which senselessly hurts performance.
R..
Where did you get this crazy idea? How's this different from `for ( i = 0; i < N; i++ )`?
Nikolai N Fetissov
+2  A: 
for ( size_t i = N ; i <= N ; i-- ) { .... }

This would do it because size_t is an unsigned int. Unsigned ints are 32bits. When the variable i has a value of 0, you want your loop to execute the condition. If you perform i--, the computer does

 00000000000000000000000000000000
-00000000000000000000000000000001

Which results in a clear overflow, giving a value of 111111111...1. For a signed two's complement integer, this value is clearly negative. However, the type of i is an unsigned int so the computer will interpret 111111...1 to be a very large positive value.

So you have a few options:

1) Do as above and make the loop terminate when overflow occurs.

2) Make the loop run from i = 0 to i <= N but use (N-i) instead of i in everywhere in your loop. For example, myArray[i] would become myArray[N-i] (off by one depending on what the value of N actually represents).

3) Make the condition of your for loop exploit the precedence of the unary -- operator. As another user posted,

for ( size_t i = N + 1 ; i-- > 0 ; ) { ... }

This will set i to N+1, check to see if the condition N+1 > 0 still holds. It does, but i-- has a side effect, so the value of i is decremented to i = N. Keep going until you get to i = 1. The condition will be test, 1 > 0 is true, the side effect occurs, then i = 0 and it executse.

Kizaru
Don't use `i<=N` as the condition. It will likely waste time reloading `N` from memory on each iteration, and if `N` happens to be `SIZE_MAX` you have an infinite loop.
R..
+1  A: 

You can use a second variable as the loop counter to make the range of iteration clear to a future reviewer.

for (size_t j=0, i=N; j<=N; ++j, --i) {
    // code here ignores j and uses i which runs from N to 0
    ...
}
RBerteig
+5  A: 

Yes, it's correct and it is a very common approach. I wouldn't consider changing it.

Arithmetic on unsigned integer types is guaranteed to use modulo 2^N arithmetic (where N is the number of value bits in the type) and behaviour on overflow is well defined. The result is converted into the range 0 to 2^N - 1 by adding or subtracting multiples of 2^N (i.e. modulo 2^N arithmetic).

-1 converted to an unsigned integer type (of which size_t is one) converts to 2^N - 1. -- also uses modulo 2^N arithmetic for unsigned types so an unsigned type with value 0 will be decremented to 2^N - 1. Your loop termination condition is correct.

Charles Bailey
+3  A: 

Personally, I would just use a different loop construct, but to each their own:

size_t i = N;
do {
    ...
} while (i --> 0);

(you could just use (i--) as the loop condition, but one should never pass up a chance to use the --> "operator").

Stephen Canon
+1 for using the `-->` operator. C code is intended to be obfuscated, and it's fun :)
Matt Joiner
+3  A: 

Just because for has a convenient place to put a test at the beginning of each iteration doesn't mean you have to use it. To handle N to 0 inclusive, the test should be at the end, at least if you care about handling the maximum value. Don't let the convenience suck you in to putting the test in the wrong place.

for (size_t i = N;; --i) {
    ...
    if (i == 0) break;
}

A do-while loop would also work but then you'd additionally give up i being scoped to the loop.

mark4o
+1  A: 
for (i=N; i+1; i--)
R..
Note that, like any solution except do/while loops and the equivalent for/break construct, this will fail if `N` is `SIZE_MAX`.
R..