У меня есть цикл, который должен пойти от N до 0 (включительно). Мой i
переменная имеет тип size_t
который обычно не подписан. Я в настоящее время использую следующий код:
for (size_t i = N; i != (size_t) -1; --i) {
...
}
Это корректно? Существует ли лучший способ обработать условие?
Спасибо,
Vincent.
Да, это правильно, и это очень распространенный подход. Я бы не стал его менять.
Арифметика для беззнаковых целочисленных типов гарантированно использует арифметику по модулю 2 ^ N
(где N
- количество битов значения в типе), а поведение при переполнении четко определено. Результат преобразуется в диапазон от 0
до 2 ^ N - 1
путем добавления или вычитания кратных 2 ^ N
(т. Е. По модулю 2 ^ N
арифметика).
-1
, преобразованный в целочисленный тип без знака (из которых size_t
- один) преобразуется в 2 ^ N - 1
. -
также использует арифметику по модулю 2 ^ N
для беззнаковых типов, поэтому беззнаковый тип со значением 0
будет уменьшен до 2 ^ N - 1
. Ваше условие завершения цикла правильное.
Поскольку беззнаковое целое число при уменьшении с нуля переходит в максимальное значение, вы можете попробовать следующее, при условии, что N
меньше этого максимального значения (кто-нибудь, пожалуйста, поправьте меня, если это UB ):
for ( size_t i = N; i <= N; i-- ) { /* ... */ }
Вы можете использовать это:
for (size_t i = n + 1; i-- > 0;)
{
}
Надеюсь, что это поможет.
Тот факт, что для
имеет удобное место для размещения теста в начале каждой итерации, не означает, что вы должны его использовать. Чтобы обработать N до 0 включительно , тест должен быть в конце, по крайней мере, если вы заботитесь об обработке максимального значения. Не позволяйте удобству втянуть вас в то, чтобы поставить тест в неправильном месте.
for (size_t i = N;; --i) {
...
if (i == 0) break;
}
Цикл do-while также будет работать, но тогда вы дополнительно откажетесь от привязки 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
...
}
Лично я бы просто использовал другую конструкцию цикла, но каждому свое:
size_t i = N;
do {
...
} while (i --> 0);
(можно просто использовать (i--)
в качестве условия цикла, но никогда не следует упускать шанс использовать "оператор" -->
).
for ( size_t i = N ; i <= N ; i-- ) { .... }
Это сработает, потому что size_t - это целое число без знака. Беззнаковые целые числа - 32 бита. Когда переменная i имеет значение 0, вы хотите, чтобы ваш цикл выполнял условие. Если вы выполните i--, компьютер выполнит
00000000000000000000000000000000
-00000000000000000000000000000001
, что приведет к явному переполнению, давая значение 111111111 ... 1. Для целого числа с дополнением до двух со знаком это значение явно отрицательное. Однако тип i - это целое число без знака, поэтому компьютер интерпретирует 111111 ... 1 как очень большое положительное значение.
Итак, у вас есть несколько вариантов:
1) Сделайте то, что указано выше, и завершите цикл при переполнении.
2) Запустите цикл от i = 0 до i <= N, но используйте (N-i) вместо i везде в вашем цикле. Например, myArray [i] превратится в myArray [N-i] (отличается на единицу в зависимости от того, что на самом деле представляет значение 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 истинно, возникает побочный эффект. , тогда i = 0, и он выполняется.