16

What's the "correct" way to write a decreasing loop with a size_t value and a boundary condition. Example incorrect implementation:

for (size_t elemNum = listSize-1; elemNum >= 0; --elemNum) { /* ... */ }

When it reaches zero it will wrap around to the max value rather than acting as a boundary condition. Iterating the loop in reverse is necessary. It seems like a problem that would have a defacto standard solution but I can't find what it is.

Mark Langen
  • 379
  • 4
  • 9

9 Answers9

22

The most succinct approach is to use post-increment:

for (size_t i = listSize; i--;) ...
Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • umm, this won't behave any different to --i? – James Aug 28 '11 at 22:37
  • 1
    oh, I see, the increment is in the condition. My bad – James Aug 28 '11 at 22:39
  • I think it works, but imo using a while loop like pmg suggested is easier to understand than "misusing" for in that way. – Medo42 Aug 28 '11 at 22:40
  • 1
    @Medo42: How is the while-loop approach any less of a "misuse"? Plus, it broadens the scope of the loop variable beyond the loop, which is bad karma. – Marcelo Cantos Aug 28 '11 at 22:43
  • 2
    I like this solution the best. So long as the "goes to" operator mentioned in the other comment is understood I can make it even more clear as: `for (size_t i = listSize; i --> 0;) { /* ... */ }` – Mark Langen Aug 29 '11 at 19:52
  • @Stravant: That's quite ingenious! I don't know if it's clearer, though. You'll have people reaching for their text books trying to figure out what the `-->` operator does. – Marcelo Cantos Aug 29 '11 at 22:32
  • @MarkLangen: except that `i` is an unsigned type and the `> 0` check misleads readers/maintainers into thinking `i` could ever be negative. By omitting the relational operator the implicit `!= 0` in this answer avoids that. – Firstrock Aug 13 '21 at 15:44
7
elemNum = listsize;
while (elemNum--) {
    /* work with elemNum varying from `listsize - 1` down to `0` */
}
pmg
  • 106,608
  • 13
  • 126
  • 198
  • 2
    Some compilers support the special "goes to" operator, `while (elemNum --> 0) { /* ... */ }` for this purpose. – Kerrek SB Aug 28 '11 at 22:50
  • 3
    @Kerrek: I think you mean all Standard conforming compilers :) – pmg Aug 28 '11 at 22:53
  • @pmg, a small nitpick here: the `elemNum` will not be 0 after this (there will be a wrap-around just after the last iteration). Since the asker's `for` loop didn't even make `elemNum` visible outside of the loop it probably does not matter, but it is (very slightly) cleaner to do the decrement as first line inside the loop. – Branko Dimitrijevic Aug 28 '11 at 23:05
  • @Branko I don't think that is a small nitpick, but a real counter argument. Local index variables in `for` loops do have a reasons, and so this answer here is not appropriate. – Jens Gustedt Aug 29 '11 at 07:03
4

I don't know of a standard way, but this should work:

for (size_t elemNum = listSize-1; elemNum < listSize; --elemNum) { /* ... */ }
Medo42
  • 3,821
  • 1
  • 21
  • 37
  • That's a pretty neat way of doing it. – James Aug 28 '11 at 22:34
  • 4
    no, it's a pretty obfuscated way. it's so ugly I would like to downvote it.. :/ – Karoly Horvath Aug 28 '11 at 22:37
  • Well, I don't see it tricky at all. I see it very elegant and intelligent. – Diego Sevilla Aug 28 '11 at 22:40
  • I think pmg's solution is the best so far, and would recommend it over mine. – Medo42 Aug 28 '11 at 22:42
  • 2
    This approach is fragile. The type of the loop variable might change later (e.g., a maintenance programmer might do so to resolve type comparison warnings, and may neglect to properly inspect the code), and then all bets are off. – Marcelo Cantos Aug 28 '11 at 22:46
  • @Medo42 This will work because `size_t` is actually guaranteed to be unsigned. However, this is not a good approach in general - the type of the index may not even be known in advance, such as inside a template. – Branko Dimitrijevic Aug 28 '11 at 22:53
  • @Daniel: Why would that be the case? When `elemNum` wraps around, it would be equal to `SIZE_MAX`, and `elemNum < listSize` would be false since they would be equal values. – Jason Aug 28 '11 at 23:30
2

You could use two variables instead:

size_t count = 0;
for (size_t elemNum = listSize-1; count < listSize; ++count, --elemNum) { /* ... */ }
Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
2
for (size_t counter = listSize; counter > 0; --counter) { 
     size_t index = counter-1;

    /* ... use `index` as an index ... */ 
}
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
2
size_t elemNum = listSize;
while (elemNum > 0) {
    --elemNum;
    // Do your work here.
}
Branko Dimitrijevic
  • 50,809
  • 10
  • 93
  • 167
1

You could use this as the condition:

elemNum != (size_t)-1

Or you could count up, and do some math (which the compiler will probably optimise out anyway) for your index:

for (size_t i = 1; i <= listSize; i++) {size_t elemNum = listSize-i; /* */}
James
  • 24,676
  • 13
  • 84
  • 130
0

If you can guarantee the starting value isn’t too large, a simple answer is: use a signed type instead.

-1

The standard C++ way would be to use a std::reverse_iterator.

Somehow within the loop you are accessing element elemNum of a list, say via list[elemNum], where list models the RandomAccessIterator concept. Suppose that list_iterator_type is the decltype of list. Your loop using reverse iterators becomes:

std::reverse_iterator<list_iterator_type> it, end = std::reverse_iterator<list_iterator_type>(list);
for (it = std::reverse_iterator<list_iterator_type>(list + listSize); it != end; ++it) {
    // `*it` is `list[elemNum]`
}
Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193