Итерация по всем целым числам без знака в цикле 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);
}
}