Подтвердить что ты не робот

Обратная итерация с переменной без знака

Я обсуждал использование 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 ; ) { ... }

Но я задаюсь вопросом, какие другие варианты есть...

4b9b3361

Ответ 1

Лично мне нравилось:

for (size_t i = n; i --> 0 ;)

Он имеет a) не смешно -1, b) проверка условия мнемоническая, c) она заканчивается подходящим смайликом.

Ответ 2

Целые числа без знака гарантированно обернутся вокруг. Они просто реализуют арифметику по модулю 2 N. Таким образом, легко читаемая идиома такова:

for (size_t i = n-1; i < n ; --i) { ... }

это устанавливает переменную в начальное значение, которое вы хотите, показывает смысл итерации (вниз) и дает точно условие для значений, которые вы хотите обработать.

Ответ 3

  • Замените цикл алгоритмом.
  • Используйте обратный итератор вместо целого числа.
  • Обратный отсчет от n до 1, но внутри цикла используйте i-1 вместо i.

Ответ 4

Используете ли вы стандартные контейнеры для библиотек? Если так мне нравится 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

Ответ 5

Если вы беспокоитесь о случайном написании цикла, некоторые компиляторы будут предупреждать о таких вещах. Например, gcc имеет предупреждение, включенное опцией -Wtype-limits (также включенной -Wextra):

x.c:42: warning: comparison of unsigned expression >= 0 is always true

Ответ 6

i != -1 полагается на -1, который молча прикладывается к size_t, который кажется мне хрупким, поэтому, альтернативы, которые вы представляете, я бы обязательно пошел с пост-декрементом. Другая возможность (особенно если вам вообще не нужно i в теле цикла, но просто нужно итерации по массиву в обратном порядке) было бы обернуть массив в контейнер std:: и использовать итератор на обертка с методами rbegin и rend. Например, Boost.Array будет поддерживать последний выбор.

Ответ 7

Вот указатель на хорошее обсуждение на эту тему.

Я бы попробовал:

for( size_t i = n; i != 0; i-- ) {
  // do stuff with array[ i - 1 ]
}

Ответ 8

size_t i = n-1;

do  { 
  ...
} while ( i-- != 0);

Вы можете обернуть это с помощью if (n > 0), если это необходимо.

Ответ 9

Еще один способ (без подписи/беззнакового сравнения):

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

так

(i + 1 > 0) === (i > -1)

Ответ 10

Другое решение (доступное в POSIX-совместимых системах), которое я считаю простым и эффективным, заключается в замене size_t на ssize_t:

for (ssize_t i = n-1; i >= 0; --i) { ... }

В не POSIX-системах ssize_t не является трудным для typedef: альтернатива ssize_t в POSIX-несовместимых системах