Чтобы условие цикла останавливалось на 0 при использовании целых чисел без знака?
У меня есть цикл, который должен идти от N до 0 (включительно). мой i
переменная имеет тип size_t
который обычно без знака. В настоящее время я использую следующий код:
for (size_t i = N; i != (size_t) -1; --i) {
...
}
Это верно? Есть ли лучший способ справиться с этим заболеванием?
Спасибо,
Винсент.
8 ответов
Да, это правильно и это очень распространенный подход. Я бы не стал менять его.
Арифметика на целочисленных типах без знака гарантированно использует модуль 2^N
арифметика (где N
число битов значения в типе) и поведение при переполнении хорошо определено. Результат конвертируется в диапазон 0
в 2^N - 1
складывая или вычитая кратные 2^N
(т.е. по модулю 2^N
арифметическое).
-1
преобразуется в целое число без знака (из которых size_t
один) превращается в 2^N - 1
, --
также использует по модулю 2^N
арифметика для беззнаковых типов, поэтому беззнаковый тип со значением 0
будет уменьшен до 2^N - 1
, Ваше условие завершения цикла верное.
Просто так for
имеет удобное место для тестирования в начале каждой итерации, но это не значит, что вы должны его использовать. Для обработки от N до 0 включительно, тест должен быть в конце, по крайней мере, если вы заботитесь о обработке максимального значения. Не позволяйте удобству втягивать вас в то, что вы поставили тест не на то место.
for (size_t i = N;; --i) {
...
if (i == 0) break;
}
Цикл do-while также будет работать, но тогда вы дополнительно сдадитесь i
быть прикованным к петле.
Лично я бы просто использовал разные конструкции цикла, но для каждого свои:
size_t i = N;
do {
...
} while (i --> 0);
(вы могли бы просто использовать (i--)
как условие цикла, но нельзя упускать шанс использовать -->
"Оператор").
Вы можете использовать это:
for (size_t i = n + 1; i-- > 0;)
{
}
Надеюсь, это поможет.
for ( size_t i = N ; i <= N ; i-- ) { .... }
Это будет сделано, потому что size_t является неподписанным int. Целые числа без знака - 32 бита. Когда переменная i имеет значение 0, вы хотите, чтобы ваш цикл выполнил условие. Если вы выполняете i--, компьютер делает
00000000000000000000000000000000
-00000000000000000000000000000001
Это приводит к явному переполнению, давая значение 111111111... 1. Для целого числа дополнения со знаком два это значение явно отрицательное. Однако тип i - это целое число без знака, поэтому компьютер будет интерпретировать 111111... 1 как очень большое положительное значение.
Итак, у вас есть несколько вариантов:
1) Сделайте, как указано выше, и заставьте цикл завершиться при переполнении.
2) Заставьте цикл работать от i = 0 до i <= N, но используйте (Ni) вместо i везде в вашем цикле. Например, myArray[i] станет myArray[Ni] (отключается на единицу в зависимости от того, что на самом деле представляет значение N).
3) Сделайте условие вашего цикла for использовать приоритет унарного оператора. Как другой пользователь размещен,
for ( size_t i = N + 1 ; i-- > 0 ; ) { ... }
Это установит для i значение N+1, проверьте, выполняется ли условие N+1 > 0. Это так, но у i-- есть побочный эффект, поэтому значение i уменьшается до i = N. Продолжайте, пока не доберетесь до i = 1. Условие будет проверено, 1 > 0 верно, побочный эффект имеет место, тогда я = 0, и это исполнение.
Вы можете использовать в качестве счетчика цикла вторую переменную, чтобы сделать диапазон повторений понятным для будущего рецензента.
for (size_t j=0, i=N; j<=N; ++j, --i) {
// code here ignores j and uses i which runs from N to 0
...
}
Поскольку целое число без знака переходит в максимальное значение при уменьшении от нуля, вы можете попробовать следующее при условии N
меньше, чем это максимальное значение (кто-то, пожалуйста, поправьте меня, если это UB):
for ( size_t i = N; i <= N; i-- ) { /* ... */ }