Чтобы условие цикла останавливалось на 0 при использовании целых чисел без знака? - PullRequest
11 голосов
/ 14 августа 2010

У меня есть цикл, который должен идти от N до 0 (включительно).Моя переменная i имеет тип size_t, который обычно не имеет знака.В настоящее время я использую следующий код:

for (size_t i = N; i != (size_t) -1; --i) {
    ...
}

Это правильно?Есть ли лучший способ справиться с этим заболеванием?

Спасибо,

Винсент.

Ответы [ 8 ]

7 голосов
/ 14 августа 2010

Да, это правильно и это очень распространенный подход. Я бы не стал менять его.

Арифметика для целочисленных типов без знака гарантированно использует арифметику по модулю 2^N (где N - число битов значения в типе), и поведение при переполнении хорошо определено. Результат преобразуется в диапазон от 0 до 2^N - 1 путем сложения или вычитания кратных 2^N (то есть по модулю 2^N арифметика).

-1, преобразованный в целочисленный тип без знака (из которых size_t - один), преобразуется в 2^N - 1. -- также использует арифметику по модулю 2^N для типов без знака, поэтому тип без знака со значением 0 будет уменьшен до 2^N - 1. Ваше условие завершения цикла верное.

5 голосов
/ 15 августа 2010

То, что for имеет удобное место для тестирования в начале каждой итерации, не означает, что вы должны его использовать. Для обработки от N до 0 включительно тест должен быть в конце, по крайней мере, если вы заботитесь о работе с максимальным значением. Не позволяйте удобству втянуть вас в неправильное место теста.

for (size_t i = N;; --i) {
    ...
    if (i == 0) break;
}

Цикл do-while также будет работать, но тогда вы дополнительно откажетесь от i, ограниченного циклом.

4 голосов
/ 15 августа 2010

Лично я бы просто использовал разные конструкции цикла, но для каждого свои:

size_t i = N;
do {
    ...
} while (i --> 0);

(вы можете просто использовать (i--) в качестве условия цикла, но нельзя упускать шансиспользовать --> «оператор»).

4 голосов
/ 14 августа 2010

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

for (size_t i = n + 1; i-- > 0;)
{
}

Надеюсь, что поможет.

3 голосов
/ 14 августа 2010
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). </p>

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, и это исполнение.

1 голос
/ 15 августа 2010
for (i=N; i+1; i--)
1 голос
/ 14 августа 2010

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

for (size_t j=0, i=N; j<=N; ++j, --i) {
    // code here ignores j and uses i which runs from N to 0
    ...
}
0 голосов
/ 14 августа 2010

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

for ( size_t i = N; i <= N; i-- ) { /* ... */ }
...