Reverse iteration with an unsigned loop variable

2020-05-11 07:27发布

I've been discussing the use of size_t with colleagues. One issue that has come up is loops that decrement the loop variable until it reaches zero.

Consider the following code:

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

This causes an infinite loop due to unsigned integer wrap-around. What do you do in this case? It seems far to easy to write the above code and not realise that you've made a mistake.

Two suggestions from our team are to use one of the following styles:

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

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

But I do wonder what other options there are...

标签: c++ c
10条回答
我想做一个坏孩纸
2楼-- · 2020-05-11 07:33

Here is a pointer to a good discussion on this topic.

I would try:

for( size_t i = n; i != 0; i-- ) {
  // do stuff with array[ i - 1 ]
}
查看更多
\"骚年 ilove
3楼-- · 2020-05-11 07:34

Personally I have come to like:

for (size_t i = n; i --> 0 ;)

It has a) no funny -1, b) the condition check is mnemonic, c) it ends with a suitable smiley.

查看更多
\"骚年 ilove
4楼-- · 2020-05-11 07:37
  1. Replace the loop with an algorithm.
  2. Use a reverse iterator instead of an integer.
  3. Count down from n to 1, but inside the loop use i-1 instead of i.
查看更多
女痞
5楼-- · 2020-05-11 07:37

Yet another way (no signed/unsigned comparisons):

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

since

(i + 1 > 0) === (i > -1)
查看更多
混吃等死
6楼-- · 2020-05-11 07:38

If you're worried about accidentally writing a loop like that, some compilers will warn about such things. For example, gcc has a warning enabled by the -Wtype-limits option (also enabled by -Wextra):

x.c:42: warning: comparison of unsigned expression >= 0 is always true
查看更多
爷、活的狠高调
7楼-- · 2020-05-11 07:39

Are you using standard library containers? If so I like reverse_iterator

   vector<int> ivect;

   // push, push, push...

   vector<int>::reverse_iterator riter;
   for(riter=riter.rbegin(); riter!=ivect.rend(); ++riter)
   {
       //...
   }

For a raw array you can just use a std::reverse_iterator the key to this is that a pointer is an iterator:

int i[] = {1, 2, 3, 4};

typedef std::reverse_iterator<const int*> irevit;

irevit iter(i+4);
irevit end(i);
for(; iter != end; ++iter) {
    cout << *iter;
}

// Prints 4321

Noncontiguous object iteration can be done by storing the object pointers in a container or array:

struct Foo {
    Foo(int i) I(i) { }
    int I;
}

vector<Foo*> foos;
for(int i = 0; i < 10; ++i)
    foos.push_back(new Foo(i));

typedef vector<Foo*>::const_reverse_iterator frevit;

frevit iter(foos.rbegin());
for(; iter != foos.rend(); ++iter) {
    cout << (*iter)->I;
}

// Prints 9876543210

If you really want to use a naked size_t then why use all of this implicitly confusing -1 trickery in the other answers? The max value of size_t is explicitly available to use as your termination value:

int is[] = {1, 2, 3, 4};
int n = 3;

for (size_t i = n; i != std::numeric_limits<size_t>::max(); --i) {
    cout << is[i] << endl;
}

// prints 4321
查看更多
登录 后发表回答