← На главную

Как правильно организовать обратный цикл в C

26.10.2026 19:28 · hackernews

Дэвид Прайвер разбирает распространённую ошибку при обратном обходе буфера в C. Если прямой цикл с индексом size_t выглядит естественно, попытка просто поменять направление часто даёт неработающий код. Условие i >= 0 для беззнаковой переменной всегда истинно, поэтому цикл не завершается после нулевого элемента. Современные компиляторы способны предупредить о таком условии, но проблема встречается по-прежнему, в том числе в коде, написанном с помощью LLM.

Отдельная ошибка возникает при пустом буфере. Выражение count - 1 вычисляется до первой итерации, и при нулевом count индекс становится SIZE_MAX. Обращение к массиву с таким индексом недопустимо. Даже непустой буфер не спасает исходный вариант от бесконечного цикла: после уменьшения нулевого беззнакового индекса происходит переход к максимальному значению типа.

Замена size_t на знаковый ssize_t устраняет видимый сбой, но добавляет другие сложности. Знаковый индекс может неожиданно участвовать в преобразованиях типов C при вычислении выражений. ssize_t относится к POSIX и не предоставляется Windows как стандартный тип, поэтому его приходится определять самостоятельно. На 32-битных платформах возможны проблемы с расширением и усечением типов. Кроме того, такую запись приходится каждый раз внимательно проверять, особенно условие остановки.

Автор предлагает короткую форму: for (size_t i = count; i--; ) с телом цикла внутри. Индекс сначала получает значение count, а в условии используется постфиксный декремент. Он возвращает прежнее значение и лишь затем уменьшает переменную. Поэтому первая итерация работает с count - 1, последняя — с нулём, а при исходном count = 0 тело не выполняется вовсе.

После финальной проверки переменная действительно переходит к максимальному беззнаковому значению, однако такое wrap-around-поведение определено стандартом C и уже не влияет на работу цикла. Внутри тела индекс всегда находится в допустимых границах, если сам буфер содержит count элементов. В примере find_last функция проверяет элементы с конца, записывает позицию найденного значения через указатель where и возвращает 1; если совпадений нет, возвращается 0.

По мнению Прайвера, преимущество этой записи не только в компактности. Она сохраняет беззнаковый тип, не требует платформенных определений и позволяет с первого взгляда отличить корректный обратный обход от ошибочного.

Читать оригинал →