Чтобы условие цикла останавливалось на 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 (i=N; i+1; i--)

Вы можете использовать в качестве счетчика цикла вторую переменную, чтобы сделать диапазон повторений понятным для будущего рецензента.

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-- ) { /* ... */ }
Другие вопросы по тегам