Ответ 1
Вы можете использовать
for (unsigned int j = n; j-- > 0; ) {}
Итерирует от n-1
до 0
.
Я хочу, чтобы переменная итератора в цикле for обратного итерации равнялась 0 как unsigned int
, и я не могу представить аналогичное сравнение с i > -1
, как это было бы, если бы это был signed int
.
for (unsigned int i = 10; i <= 10; --i) { ... }
Но это кажется очень неясным, поскольку он полагается на числовое переполнение целого числа без знака, которое должно быть выше 10.
Может быть, у меня просто нет четкой головы, но лучше ли это сделать...
Отказ от ответственности: это простой пример использования, верхний предел 10 тривиален, он может быть любым, а i
должен быть unsigned int
.
Вы можете использовать
for (unsigned int j = n; j-- > 0; ) {}
Итерирует от n-1
до 0
.
Следующее делает то, что вы хотите:
for (unsigned i = 10; i != static_cast<unsigned>(-1); --i)
{
// ...
}
Это отлично определено и фактически работает. Арифметика на подписанных типах точно определяется стандартом. Действительно:
От 4.7/2 (относительно приведения к неподписанному типу):
Если тип назначения не указан, результирующее значение представляет собой наименьшее беззнаковое целое, совпадающее с исходным целым (по модулю 2 ^ n, где n - количество бит, используемых для представления неподписанного типа)
и 3.9.1/4
Беззнаковые целые числа, объявленные без знака, должны подчиняться законам арифметики по модулю 2 ^ n, где n - количество бит в представлении значений этого конкретного размера целого числа
Мой шаблон для этого обычно...
for( unsigned int i_plus_one = n; i_plus_one > 0; --i_plus_one )
{
const unsigned int i = i_plus_one - 1;
// ...
}
Действительно ли вы выполняете итерацию с какого-то числа больше std::numeric_limits<int>::max()
? Если нет, я бы предложил просто использовать обычный int
в качестве переменной цикла и static_cast
для unsigned
в местах вашего кода, которые ожидают, что он будет неподписанным. Таким образом, вы можете использовать интуитивное условие >= 0
или > -1
, и в целом я ожидаю, что он будет более читабельным, чем любая из неподписанных альтернатив.
static_cast
просто должен сказать компилятору, как работать с переменной и вообще не иметь никаких последствий для производительности.
Я бы использовал две переменные:
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 --) {
// ...
}
for(unsigned i = x ; i != 0 ; i--){ ...
И если вы хотите выполнить тело цикла при я == 0 и остановитесь после этого. Просто начните с i = x+1;
Кстати, почему я должен быть без знака?
Я могу думать, что эти два варианта - это либо литые, либо поющие числа (может быть сделано неявно, например, сравнение с -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, или вы могли бы завершить бесконечный цикл.
Просто:
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; // ...
}
Нет
Вы можете попробовать и определить следующий макрос:
#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;
}
Он может использоваться для итерации назад и вперед через unsigned, целые числа, перечисления и символы:
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;
}
Несмотря на свое неудобное определение, он оптимизирован очень хорошо. Я посмотрел на дизассемблер в VС++. Код чрезвычайно эффективен. Не откладывайте, а три для операторов: компилятор будет производить только один цикл после оптимизации! Вы даже можете определить замкнутые контуры:
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;
}
Вы, очевидно, не можете перебирать нумерованные типы с пробелами.
Еще один способ:
for(unsigned i = n-1; i < n ; --i)
{
// Iterates from n-1 to 0
}
Simillarly для size_t (беззнаковый целочисленный тип) использует тот же трюк
for(std::size_t i = n-1; i < n ; --i)
{
// Iterates from n-1 to 0
}