Итерация по всем целым числам без знака в цикле for

предположим, я хочу перебрать все целые числа в for петли. Для обсуждения предположим, что я вызываю какую-то неизвестную функцию f(unsigned x) для каждого целого числа:

for (unsigned i = 0; i < UINT_MAX; i++) {
     f(i);
}

разумеется, не перебирайте все целые числа, потому что он пропускает один: UINT_MAX. Изменяя условие i <= UINT_MAX просто приводит к бесконечному циклу, потому что это тавтология.

вы можете сделать это с помощью do-while петля, но вы теряете все тонкости for синтаксис.

можно мне мой торт (for петли) и съесть его тоже (перебирать все целые числа)?

6 ответов


вы можете сделать это с помощью цикла do-while, но вы теряете все тонкости синтаксис for.

он по-прежнему выполним с циклом do-while, используя анонимную область блока:

{
    unsigned i = 0;
    do { f(i); } while (++i != 0);
}

хотя эта конструкция может быть не самой идиоматичной, она является очевидным кандидатом на четкий код сборки. Например, gcc -O компилирует его как:

.L2:
        mov     edi, ebx   ; ebx starts with zero
        call    f
        add     rbx, 1
        cmp     rbx, rbp   ; rbp is set with 4294967296
        jne     .L2

вам нужно будет выполнить тест в конце тела цикла, как do-while:

for (unsigned int i = 0; /* nothing */; i++) {
    ...
    if (i == UINT_MAX) {
        break;
    }
}

для теста в стандартной для работы тестовой позиции цикла вам нужно будет отслеживать текущую итерацию таким образом, чтобы можно было различать состояния UINT_MAX+2: по одному для каждого входа в тело цикла и один для одного раза, когда вы этого не делаете. Один unsigned int не может справиться с этим, поэтому вам понадобится хотя бы одна вспомогательная переменная или больший счетчик циклов.


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

for (unsigned int i = 0, repeat = 0; !(i == 0 && repeat); i++, repeat = 1) {
    ...
}

классический способ эффективно реализовать итерацию с помощью одного теста-это do / while петли:

unsigned i = 0;
do { f(i); } while (i++ != UINT_MAX);

если вы настаиваете на использовании for петли:

for (unsigned i = 0;; i++) {
    f(i);
    if (i == UINT_MAX)
        break;
}

вот еще один вариант с 2 переменными, где вся логика находится внутри for выражения:

for (unsigned int i = 0, not_done = 1; not_done; not_done = (i++ - UINT_MAX)) {
    f(i);
}

это может привести к более медленному коду из-за дополнительной переменной, но, как прокомментировал BeeOnRope,clang и icc скомпилировать его в очень эффективный код.


простым решением было бы,

unsigned i;
for (i=0; i<UINT_MAX; i++) {
  f(i);
}
f(i);  // i will be UINT_MAX at this time.

используйте больший целочисленный тип:

#include <limits.h>
#include <stdio.h>

int main() {
    for (unsigned long i = 0; i <= UINT_MAX; i++) {
        f(i);
    }
}

эта версия использует stdint для большей последовательности

#include <stdio.h>
#include <stdint.h>

int main() {
    for (uint_fast64_t i = 0; i <= UINT32_MAX; ++i) {
        f(i);
    }
}