Каков наилучший способ сделать обратный цикл " for " с беззнаковым индексом?


моя первая попытка обратный цикл что делает что-то n раз было что-то вроде:

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

этой не потому что арифметика без знакаi гарантированно всегда больше или равно нулю, поэтому условие цикла всегда будет истинным. К счастью, компилятор gcc предупредил меня о "бессмысленном сравнении", прежде чем мне пришлось задаться вопросом, почему цикл выполняется бесконечно.


Я ищу элегантный способ решения этой проблемы, имея в виду, что:

  1. это должен быть обратный цикл.
  2. индекс цикла должен быть без знака.
  3. N-константа без знака.
  4. он не должен основываться на "неясной" кольцевой арифметике беззнаковых целых чисел.

какие идеи? Спасибо :)

20 51

20 ответов:

Как насчет:

for (unsigned i = n ; i-- > 0 ; )
{
  // do stuff with i
}
for ( unsigned int loopIndex = n; loopIndex > 0; --loopIndex ) {
    unsigned int i = loopIndex - 1;
    ...
} 

или

for ( unsigned int loopIndex = 0; loopIndex < n; ++loopIndex ) {
    unsigned int i = n - loopIndex - 1;
    ...
} 
for ( unsigned int i = n; i != 0; i-- ) {
    // do something with i - 1
    ...     
}

обратите внимание, что если вы используете C++, а также C, используя != это хорошая привычка, чтобы попасть в, когда вы переключиться на использование итераторов, где

for ( unsigned int i = n; i > 0; i-- ) {
    ...  
    i-1 //wherever you've been using i   
}

Я бы использовал

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

это почти то же самое, что и ответ skizz (он пропускает окончательное ненужное уменьшение, но компилятор должен оптимизировать это), и фактически пройдет проверку кода. Каждый стандарт кодирования, с которым мне приходилось работать, не имел мутации в условном правиле.

почему не просто:

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

Это соответствует всем требованиям, перечисленным в теле вопроса. Он не использует ничего, что может привести к сбою проверки кода или нарушению стандарта кодирования. Единственное возражение, которое я мог бы увидеть, - это если ОП действительно настаивал на for цикл и не простой способ генерации i = (n-1) .. 0.

может, так? ИМХО его ясно и читабельно. Вы можете опустить if (n>=1), если он неявно известен каким-либо образом.

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 );
}

другая версия:

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

    } while( i != 0 );
}

первый код без оператора if будет выглядеть так:

unsigned int i = n-1;
do {

} while( i-- != 0 );

или вы можете положиться на поведение обертывания unsigned int Если вам нужна индексация от N-1 до 0

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

конечно не элегантно, но это работает.

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

хорошо, его "неясная арифметика кольца".

единственная причина, по которой я упоминаю этот параметр, заключается в том, что я не видел его в списке.

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

категорически против интуиции, но это работает. причина это работает, потому что вычитая 1 из 0 дает наибольшее число, которое может быть представлено целым числом без знака.

вообще я не думаю, что это хорошая идея, чтобы работать с целыми числами без знака и артметики, особенно при вычитании.

легко, просто остановитесь на -1:

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

edit: не уверен, почему это становится downvoted. это работает, и это проще и очевиднее, чем любой из вышеперечисленных.

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

должно работать нормально. Если вам нужно использовать i переменная как индекс в массиве сделать это так:

array[i-1];

тю. Вот ваши варианты:

  1. использовать i=0 поскольку ваше условие разрыва - цикл не будет выполняться, когда я достигну 0, поэтому выполните 1 итерацию содержимого цикла для i=0 после того, как был произведен выход из цикла.
for ( unsigned int i = n-1; i > 0; i-- ) {
    doStuff(i);
}
doStuff(0);
  1. в цикле, тест на i=0 и break выход. Не рекомендуется, потому что теперь вы проверяете значение i дважды в цикле. Также использование разрыва в цикле обычно считается плохим практиковать.
for ( unsigned int i = n-1; i >= 0; i-- ) {
    doStuff(i);
    if (i=0) break;
}
unsigned index;
for (unsigned i=0; i<n; i++)
{
    index = n-1 - i; // {i == 0..n-1} => {index == n-1..0}
}

Это непроверенно, но не могли бы вы сделать следующее:

for (unsigned int i, j = 0; j < n; i = (n - ++j)) {
    /* do stuff with i */
}

используйте две переменные, одну для подсчета до, а другой для индекса массива:

unsigned int Index = MAX - 1;
unsigned int Counter;
for(Counter = 0; Counter < MAX; Counter++)
{
    // Use Index
    Index--;
}

поскольку это не стандарт для цикла, я бы, вероятно, использовал цикл while вместо этого, например:

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

     if (i == 0)
    {
        break;
    }
    i--;
}
for ( unsigned int i = n-1; (n-i) >= 0; i-- ) {
    // n-i will be negative when the loop should stop.
    ...     
}

Эл.я:

#define unsigned signed

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