Ответ 1
Лично мне нравилось:
for (size_t i = n; i --> 0 ;)
Он имеет a) не смешно -1
, b) проверка условия мнемоническая, c) она заканчивается подходящим смайликом.
Я обсуждал использование size_t с коллегами. Одна из проблем, которая возникла, - это циклы, которые уменьшают переменную цикла до тех пор, пока она не достигнет нуля.
Рассмотрим следующий код:
for (size_t i = n-1; i >= 0; --i) { ... }
Это вызывает бесконечный цикл из-за отсутствия целого числа без знака. Что вы делаете в этом случае? Кажется, очень легко написать вышеприведенный код и не осознавать, что вы допустили ошибку.
Два предложения нашей команды - использовать один из следующих стилей:
for (size_t i = n-1; i != -1 ; --i) { ... }
for (size_t i = n; i-- > 0 ; ) { ... }
Но я задаюсь вопросом, какие другие варианты есть...
Лично мне нравилось:
for (size_t i = n; i --> 0 ;)
Он имеет a) не смешно -1
, b) проверка условия мнемоническая, c) она заканчивается подходящим смайликом.
Целые числа без знака гарантированно обернутся вокруг. Они просто реализуют арифметику по модулю 2 N. Таким образом, легко читаемая идиома такова:
for (size_t i = n-1; i < n ; --i) { ... }
это устанавливает переменную в начальное значение, которое вы хотите, показывает смысл итерации (вниз) и дает точно условие для значений, которые вы хотите обработать.
i-1
вместо i
.Используете ли вы стандартные контейнеры для библиотек? Если так мне нравится reverse_iterator
vector<int> ivect;
// push, push, push...
vector<int>::reverse_iterator riter;
for(riter=riter.rbegin(); riter!=ivect.rend(); ++riter)
{
//...
}
Для необработанного массива вы можете просто использовать std::reverse_iterator
, ключ к этому заключается в том, что указатель является итератором:
int i[] = {1, 2, 3, 4};
typedef std::reverse_iterator<const int*> irevit;
irevit iter(i+4);
irevit end(i);
for(; iter != end; ++iter) {
cout << *iter;
}
// Prints 4321
Непрерывная итерация объектов может быть выполнена путем хранения указателей объектов в контейнере или массиве:
struct Foo {
Foo(int i) I(i) { }
int I;
}
vector<Foo*> foos;
for(int i = 0; i < 10; ++i)
foos.push_back(new Foo(i));
typedef vector<Foo*>::const_reverse_iterator frevit;
frevit iter(foos.rbegin());
for(; iter != foos.rend(); ++iter) {
cout << (*iter)->I;
}
// Prints 9876543210
Если вы действительно хотите использовать голый size_t
, то зачем использовать все эти неявно запутывающие -1 трюки в других ответах? Максимальное значение size_t
явно доступно для использования в качестве вашего значения завершения:
int is[] = {1, 2, 3, 4};
int n = 3;
for (size_t i = n; i != std::numeric_limits<size_t>::max(); --i) {
cout << is[i] << endl;
}
// prints 4321
Если вы беспокоитесь о случайном написании цикла, некоторые компиляторы будут предупреждать о таких вещах. Например, gcc имеет предупреждение, включенное опцией -Wtype-limits
(также включенной -Wextra
):
x.c:42: warning: comparison of unsigned expression >= 0 is always true
i != -1
полагается на -1
, который молча прикладывается к size_t
, который кажется мне хрупким, поэтому, альтернативы, которые вы представляете, я бы обязательно пошел с пост-декрементом. Другая возможность (особенно если вам вообще не нужно i
в теле цикла, но просто нужно итерации по массиву в обратном порядке) было бы обернуть массив в контейнер std::
и использовать итератор на обертка с методами rbegin
и rend
. Например, Boost.Array будет поддерживать последний выбор.
Вот указатель на хорошее обсуждение на эту тему.
Я бы попробовал:
for( size_t i = n; i != 0; i-- ) {
// do stuff with array[ i - 1 ]
}
size_t i = n-1;
do {
...
} while ( i-- != 0);
Вы можете обернуть это с помощью if (n > 0)
, если это необходимо.
Еще один способ (без подписи/беззнакового сравнения):
for (size_t i = n-1; i + 1 > 0; i--)
так
(i + 1 > 0) === (i > -1)
Другое решение (доступное в POSIX-совместимых системах), которое я считаю простым и эффективным, заключается в замене size_t на ssize_t:
for (ssize_t i = n-1; i >= 0; --i) { ... }
В не POSIX-системах ssize_t не является трудным для typedef: альтернатива ssize_t в POSIX-несовместимых системах