Неподписанная int обратная итерация с циклами for - PullRequest
36 голосов
/ 28 марта 2011

Я хочу, чтобы переменная итератора в цикле for переворачивала итерацию до 0 как unsigned int, и я не могу думать о аналогичном сравнении с i > -1, как вы бы сделали, если бы это было signed int.

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

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

Может быть, у меня просто нет ясной головы, ноКакой лучший способ сделать это ...

Отказ от ответственности: это простой случай использования, верхний предел 10 тривиален, это может быть что угодно, и i долженбыть unsigned int.

Ответы [ 11 ]

33 голосов
/ 28 марта 2011

Вы можете использовать

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

Итерация от n-1 до 0.

18 голосов
/ 28 марта 2011

Следующее делает то, что вы хотите:

for (unsigned i = 10; i != static_cast<unsigned>(-1); --i)
{
    // ...
}

Это отлично определено и фактически работает. Арифметика на знаковых типах четко определяется стандартом. Действительно:

С 4,7 / 2 (относительно приведения к типу без знака):

Если тип назначения не имеет знака, полученное значение представляет собой целое число с наименьшим числом без знака, совпадающее с целым числом источника (по модулю 2 ^ n, где n - число битов, используемых для представления типа без знака)

и 3.9.1 / 4

Целые числа без знака, объявленные как без знака, должны подчиняться законам арифметики по модулю 2 ^ n, где n - количество битов в представлении значения этого конкретного размера целого числа

3 голосов
/ 28 марта 2011

Вы действительно итерируете с некоторого числа, большего std::numeric_limits<int>::max()? Если нет, я бы на самом деле предложил просто использовать обычный int в качестве переменной цикла и static_cast до unsigned в тех местах кода, которые ожидают, что он не будет подписан. Таким образом, вы можете использовать интуитивно понятное условие >= 0 или > -1, и в целом я ожидаю, что оно будет более читабельным, чем любая из неподписанных альтернатив.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *.

3 голосов
/ 28 марта 2011

Мой шаблон для этого обычно ...

for( unsigned int i_plus_one = n; i_plus_one > 0; --i_plus_one )
{
    const unsigned int i = i_plus_one - 1;
    // ...
}
2 голосов
/ 28 марта 2011

Я могу подумать, что эти два варианта являются либо приведенными, либо опущенными числами (например, это можно сделать неявно, сравнивая с -1), либо используйте условие цикла для проверки переполнения следующим образом:

for(unsigned i=10;i>i-1;--i){ } // i = 10, 9, ... , 1
for(unsigned i=10;i+1>i;--i){ } // i = 10, 9, ... , 1,0

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

1 голос
/ 28 марта 2011

Я бы использовал две переменные:

unsigned int start = 10;
for (unsigned int j = 0, i = start; j <= start; ++ j, -- i) {
    // ...
}

Вы также можете использовать цикл while:

unsigned int start = 10;
unsigned int i = start + 1;
while (i --) {
    // ...
}
0 голосов
/ 13 февраля 2019

Я новичок в C ++, мой ответ может быть очень глупым, но я делаю это для такого рода обратных циклов;

size_t count = 3;
size_t newCount = 0;
if (count > 0)
    for (size_t i = count - 1; i >= newCount; i--)
    {
        //do your work here

        //and at the end
        if (i == 0)
            break;
    }

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

0 голосов
/ 26 мая 2017

Еще один способ:

for(unsigned i = n-1; i < n ; --i) 
{       
    // Iterates from n-1 to 0
}

Одновременно для size_t (беззнаковый целочисленный тип) используйте тот же трюк

for(std::size_t i = n-1; i < n ; --i) 
{       
    // Iterates from n-1 to 0
}
0 голосов
/ 10 декабря 2011

Вы можете попытаться определить следующий макрос:

#define for_range(_type, _param, _A1, _B1) \
    for (_type _param = _A1, _finish = _B1,\
    _step = static_cast<_type>(2*(((int)_finish)>(int)_param)-1),\
    _stop = static_cast<_type>(((int)_finish)+(int)_step); _param != _stop; \
_param = static_cast<_type>(((int)_param)+(int)_step))

Теперь вы можете использовать его:

for_range (unsigned, i, 10,0)
{
    cout << "backwards i: " << i << endl;
}

Он может использоваться для итерации вперед и назад через целые числа без знака,enums и chars:

for_range (char, c, 'z','a')
{
    cout << c << endl;
}

enum Count { zero, one, two, three }; 

for_range (Count, c, zero, three)
{
    cout << "forward: " << c << endl;
}

Несмотря на неудобное определение, он очень хорошо оптимизирован.Я посмотрел на дизассемблер в VC ++.Код чрезвычайно эффективен.Не стоит откладывать, но три для операторов: компилятор будет производить только один цикл после оптимизации!Вы даже можете определить вложенные циклы:

unsigned p[4][5];

for_range (Count, i, zero,three)
    for_range(unsigned int, j, 4, 0)
    {   
        p[i][j] = static_cast<unsigned>(i)+j;
    }

Очевидно, что вы не можете перебирать перечисляемые типы с пробелами.

0 голосов
/ 28 марта 2011

Просто:

int start = 10;
for(unsigned int iPlus1 = start + 1 ; iPlus1 > 0 ; iPlus1--) {
  // use iPlus1 - 1 if you need (say) an array index
  a[iPlus1 - 1] = 123; // ...
}

Нет?

...