69

My first attempt of reverse for loop that does something n times was something like:

for ( unsigned int i = n-1; i >= 0; i-- ) {
    ...     
}

This fails because in unsigned arithmetic i is guaranteed to be always greater or equal than zero, hence the loop condition will always be true. Fortunately, gcc compiler warned me about a 'pointless comparison' before I had to wonder why the loop was executing infinitely.


I'm looking for an elegant way of resolving this issue keeping in mind that:

  1. It should be a backwards for loop.
  2. The loop index should be unsigned.
  3. n is unsigned constant.
  4. It should not be based on the 'obscure' ring arithmetics of unsigned integers.

Any ideas? Thanks :)

Auron
  • 13,626
  • 15
  • 47
  • 54

20 Answers20

116

How about:

for (unsigned i = n ; i-- > 0 ; )
{
  // do stuff with i
}
Skizz
  • 69,698
  • 10
  • 71
  • 108
  • 1
    Pretty good! Derived from http://stackoverflow.com/questions/275994/whats-the-best-way-to-do-a-backwards-loop-in-c-c-c/276056#276056. The only problem I see is that i is never equal to n, which seems counterintuitive looking at the loop declaration. – Auron Mar 20 '09 at 12:00
  • 4
    @Auron, n is typically the length of an array, so in most cases you don't want i == n. – quinmars Mar 20 '09 at 12:07
  • 1
    I still feel it requires a _much_ harder brainparse than the solution where you explicitly distinguish between your counter and your index. – xtofl Mar 20 '09 at 13:10
  • I support the solution, because once you had brainparsed the "i-- > 0" (which isn't that hard!) you can forget about entire problem and just use i. –  Mar 20 '09 at 13:16
  • 10
    As far as I'm concerned this is the standard idiom for reverse loop, I'm surprised people haven't met it before. – bobince Mar 20 '09 at 13:33
  • 1
    You can also do "for (unsigned i = n ; i-- ; )" but that is starting to look a bit weird, as in, an error - putting the loop update in the wrong place. Putting the "> 0" implies the "i--" is intentional. In my opinion anyway. – Skizz Mar 20 '09 at 14:29
  • It might be nice to mark this kind of construct with a nice comment to help people through the mental speedbump. – Chris Lutz Sep 06 '09 at 22:07
  • Wow, awesome! It's constructs like this that make me admire the C/C+++ languages. And also marvel at the ability to shoot yourself in the foot in them. :P – Vilx- Sep 06 '09 at 22:33
  • 6
    You can even write it as `i --> 0` which will tell visually the intention ;-) – Patrick Schlüter May 11 '11 at 08:22
  • I like this answer. In practice, I may adopt this way rather than the i-1 index way. I believe that, technically i-- invokes UB when i==0 for an unsigned. UB could include an exception and stack trace, if we had a compiler which coded underflow detection. – Heath Hunnicutt Sep 11 '11 at 23:51
  • 5
    @HeathHunnicutt - Unsigned integer arithmetic has well defined "modulo" semantics. Out of bounds results will silently wrap. – Mankarse Oct 16 '11 at 09:08
  • @Mankarse -- I use that undefined behavior, myself. If you read the C standard, you will see. Not only is this behavior not well defined, it is not defined at all. The C standard allows a compiler to treat this as a runtime error along the lines of SIGSEGV. Since it might be nice if C compilers could terminate programs which underflow and overflow, it's important for people to realize that it's a possibility, according to the definition of C. – Heath Hunnicutt Oct 16 '11 at 14:54
  • 3
    @HeathHunnicutt unsigned integers never overflow or underflow in C. Only for signed integers underflows and overflows have undefined behavior. From the standard: `unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.`. – Ruslan Mar 03 '16 at 05:21
15
for ( unsigned int loopIndex = n; loopIndex > 0; --loopIndex ) {
    unsigned int i = loopIndex - 1;
    ...
} 

or

for ( unsigned int loopIndex = 0; loopIndex < n; ++loopIndex ) {
    unsigned int i = n - loopIndex - 1;
    ...
} 
Lou Franco
  • 87,846
  • 14
  • 132
  • 192
  • Yikes! I accidentally deleted a comment when trying to delete my own. Got from my cache: "A classic example of long variable names making the code less readable, IMHO." -- sorry, can't find an undo – Lou Franco Mar 20 '09 at 11:40
12

Why not simply:

unsigned int i = n;
while(i--)
{ 
    // use i
}

This meets all the requirement enumerated in the body of the question. It doesn't use anything likely to fail code review or violate a coding standard. The only objection I could see to it is if the OP really insisted on a for loop and not a straightforward way of generating i = (n-1) .. 0.

idz
  • 12,825
  • 1
  • 29
  • 40
  • Elegant and simple solution, although a bit obscure, in my opinion. In general, I would try to avoid code with ++ or -- operators, where I have to think twice to find out what is really happening there. – Auron May 20 '11 at 07:34
  • 4
    @Auron When I learnt C understanding the difference between `++i` and `i++` was not considered obscure. How times have changed. With a upvote of 19 this is considered easier `for (unsigned i = n ; i-- > 0 ; )`... Oh well! – idz May 20 '11 at 09:55
  • 1
    Neither it was when I learnt C. I also think the most upvoted answer is a bit obscure too. More than two years have passed, and maybe today I wouldn't have voted it as recommended. Nevertheless, I upvoted your answer, and I'd suggest `while (i-- != 0)` as a clearer way to write the loop condition. – Auron May 24 '11 at 14:09
11
for ( unsigned int i = n; i != 0; i-- ) {
    // do something with i - 1
    ...     
}

Note that if you use C++ as well as C, using != is a good habit to get into for when you switch to using iterators, where <= etc. may not be available.

  • 1
    plus, when any code within the loop decreases i even further, you'll notice an infinite loop soon. (and it's easier to reason about) – xtofl Mar 20 '09 at 12:38
10

I'd tend to use

 for ( unsigned int i = n; i > 0; )  {
    --i;
    ...     
 }

it's almost the same as skizz' answer, (it misses out a final unnecessary decrement, but the compiler should optimise that away), and actually will pass code review. Every coding standard I've had to work with has had a no-mutation in conditional rule.

Pete Kirkham
  • 48,893
  • 5
  • 92
  • 171
  • 2
    Now _that_'s a mutilated for construct, to be swiftly replaced by a while statement! -1 for that. – xtofl Mar 20 '09 at 12:39
  • xtofl -- No, not a while statement. Skizz shows the best way. A while statement needs its loop variable declared outside its scope, in an independent statement. – Svante Mar 20 '09 at 12:55
  • Unfortunately, Skizz' answer wouldn't get past code review in most shops. – Pete Kirkham Mar 20 '09 at 13:03
  • 1
    "no-mutation in conditional" - I thought we were programmers, not (code)monkeys! Rules should be broken wherever the code would suffer because of it. Using an 'i-1' indexer is far worse than a mutating conditional. The hoops some of these answers jump through are just ugly. – Skizz Mar 20 '09 at 14:22
  • I agree about using a i-1 indexer being worse. – Pete Kirkham Mar 20 '09 at 14:48
  • I tend to use the `i-->0` form quite often now that I'm not in shop with rule based review. – Pete Kirkham Jul 20 '11 at 15:30
8
for ( unsigned int i = n; i > 0; i-- ) {
    ...  
    i-1 //wherever you've been using i   
}
vartec
  • 131,205
  • 36
  • 218
  • 244
  • 4
    I personally find that using "i-1" instead of just "i" is non-obvious without a big warning comment and is likely to cause strange bugs when someone in the future instinctively uses "i" instead of "i-1". – Skizz Mar 20 '09 at 12:20
  • yeah, just like using 0 to accessing first cell of an array. Life ain't easy ;-) – vartec Mar 20 '09 at 12:28
  • 1
    You can get around that by calling i 'counter' and declaring a variable called 'index = counter-1'. – xtofl Mar 20 '09 at 12:36
5

Maybe this way? IMHO its clear and readable. You can omit the if(n>=1) if it is implicitly known somehow.

if(n>=1) {
    // Start the loop at last index
    unsigned int i = n-1;
    do {
       // a plus: you can use i, not i-1 here
    } while( i-- != 0 );
}

Another version:

if(n>=1) {
    unsigned int i = n;
    do {
       i--;

    } while( i != 0 );
}

The first code without if statement would look like:

unsigned int i = n-1;
do {

} while( i-- != 0 );
4
for (unsigned int i = n-1; i<(unsigned int)-1; i--)

OK, its "obscure ring arithmetic".

Eric Bainville
  • 9,738
  • 1
  • 25
  • 27
4

Or you could rely on the wrapping behaviour of unsigned int if you need indexing from n-1 to 0

for(unsigned int i = n-1; i < n; i--) {
    ...
}
peje
  • 350
  • 1
  • 3
  • 13
3
for ( unsigned int i = n; i > 0; i-- ) {
    unsigned int x = i - 1;
    // do whatever you want with x    
}

Certainly not elegant, but it works.

itsmatt
  • 31,265
  • 10
  • 100
  • 164
3

The only reason I mention this option is because I did not see it in the list.

for ( unsigned int i = n-1; i < n; i-- ) {
... 
}

Totally against intuition, but it works. the reason it works is because subtracting 1 from 0 yields the largest number that can be represented by an unsigned integer.

In general I do not think it is a good idea to work with unsigned integers and arthmetic, especially when subtracting.

Renze de Waal
  • 533
  • 2
  • 5
  • This makes me wonder if it is a good idea or not to work with unsigned arithmetic in for loops... – Auron Mar 21 '09 at 20:04
  • 1
    Mmmm... according to the time stamp, @peje was there 3 minutes before you did... but I wonder how you two ended up with *exactly* the same code???!!! – ysap Mar 27 '12 at 02:59
2

Easy, just stop at -1:

for( unsigned int i = n; i != -1; --i )
{
 /* do stuff with i */
}

edit: not sure why this is getting downvoted. it works and it's simpler and more obvious than any of the above.

  • 1
    It doesn't work because `i` is unsigned and you should not be comparing it to a signed value. My C compiler has the warnings turned up high enough not to accept code like this. If you used a cast like `(unsigned)-1` it would work, but why not use the more obvious `UINT_MAX` macro? If I saw this in someone's code as is, I would be scratching my head trying to figure out why it wasn't an infinite loop. It works, but it's not obvious. – Chris Lutz Sep 06 '09 at 22:54
  • 1
    I definitely like the UINT_MAX solution or even the cast. Love it! I used to use what Pete Kirkham suggests, but this is so much better! – the swine Mar 17 '13 at 17:00
1
for ( unsigned int i = n; i > 0; i-- ) {
    ...     
}

Should work fine. If you need to use the i variable as an index into an array do it like this:

array[i-1];
arul
  • 13,998
  • 1
  • 57
  • 77
1

Hm. Here are your options:

  1. Use i=0 as your break condition - Loop will not execute when i reaches 0, so execute 1 iteration of the loop contents for i=0 after the loop has exited.
for ( unsigned int i = n-1; i > 0; i-- ) {
    doStuff(i);
}
doStuff(0);
  1. In the loop, test for i=0 and break out. Not recommended because now you're testing the value of i twice in the loop. Also using break within a loop is generally regarding as bad practice.
for ( unsigned int i = n-1; i >= 0; i-- ) {
    doStuff(i);
    if (i=0) break;
}
Tim
  • 402
  • 3
  • 10
  • I think repeating the doStuff just because you're working with unsigned int is a bit... overhead? Just as testing for one special case of something that's actually not special at all. – xtofl Mar 20 '09 at 12:41
1
unsigned index;
for (unsigned i=0; i<n; i++)
{
    index = n-1 - i; // {i == 0..n-1} => {index == n-1..0}
}
user80452
  • 23
  • 2
0

Since this is not a standard for loop I would probably use a while loop instead, e.g.:

unsigned int i = n - 1;
while (1)
{
    /* do stuff  with i */

     if (i == 0)
    {
        break;
    }
    i--;
}
starblue
  • 55,348
  • 14
  • 97
  • 151
  • If `n` is 0 initially, this suffers from the same issue the OP asked about, as `i` will be a very large number on the 1st loop iteration. – Remy Lebeau Jun 30 '23 at 23:23
0

This is untested, but could you do the following:

for (unsigned int i, j = 0; j < n; i = (n - ++j)) {
    /* do stuff with i */
}
SpoonMeiser
  • 19,918
  • 8
  • 50
  • 68
0

Use two variables, one to count up, and the other for the array index:

unsigned int Index = MAX - 1;
unsigned int Counter;
for(Counter = 0; Counter < MAX; Counter++)
{
    // Use Index
    Index--;
}
Steve Melnikoff
  • 2,620
  • 1
  • 22
  • 24
-1
for ( unsigned int i = n-1; (n-i) >= 0; i-- ) {
    // n-i will be negative when the loop should stop.
    ...     
}
Paulo Guedes
  • 7,189
  • 5
  • 40
  • 60
-6

e.z:

#define unsigned signed

for ( unsigned int i = n-1; i >= 0; i-- ) { ... 
}
Jason Plank
  • 2,336
  • 5
  • 31
  • 40