Цикл "для", который кажется практически бесконечным

Я отлаживаю некоторый код на данный момент, и я натолкнулся на эту строку:

for (std::size_t j = M; j <= M; --j)

(Написал мой босс, который в отпуске.)

Мне кажется странным.

Что он делает? Для меня это выглядит как бесконечный цикл.

Ответ 1

std::size_t гарантируется стандартом С++ как тип unsigned. И если вы уменьшаете тип unsigned от 0, стандарт гарантирует, что результатом этого является наибольшее значение для этого типа.

Это значение обернутого значения всегда больше или равно M 1 поэтому цикл завершается.

So j <= M, когда применяется к типу unsigned, является удобным способом сказать: "Запустите цикл до нуля, затем остановите".

Существуют альтернативы, такие как запуск j один больше, чем вы хотите, и даже использование оператора слайда for (std::size_t j = M + 1; j --> 0; ){, которые, возможно, более ясны, хотя требуют более типичного ввода. Я предполагаю, что один из недостатков (за исключением недоумения, который он производит при первой проверке), заключается в том, что он плохо переносится на языки без беззнаковых типов, таких как Java.

Обратите внимание также, что схема, которую ваш босс выбрал, "заимствует" возможное значение из набора unsigned: в этом случае так происходит, что M, установленный в std::numeric_limits<std::size_t>::max(), не будет иметь правильное поведение. Фактически, в этом случае цикл бесконечен. (Это то, что вы наблюдаете?) Вы должны вставить комментарий к этому эффекту в код и, возможно, даже утверждать в этом конкретном состоянии.


1 Подлежит M не std::numeric_limits<std::size_t>::max().

Ответ 2

То, что ваш босс, вероятно, пытался сделать, это считать от M до нуля включительно, выполняя некоторые действия для каждого номера.

К сожалению, есть крайний случай, когда это действительно даст вам бесконечный цикл, тот, где M - максимальное значение size_t, которое вы можете иметь. И хотя он хорошо определил, что будет делать беззнаковое значение, когда вы уменьшите его с нуля, я утверждаю, что сам код является примером небрежного мышления, тем более, что существует совершенно жизнеспособное решение без недостатков вашей попытки боссов.

Этот более безопасный вариант (и более читаемый, на мой взгляд, при сохранении ограниченного ограничения по объему):

{
    std::size_t j = M;
    do {
        doSomethingWith(j);
    } while (j-- != 0);
}

В качестве примера см. следующий код:

#include <iostream>
#include <cstdint>
#include <climits>
int main (void) {
    uint32_t quant = 0;
    unsigned short us = USHRT_MAX;
    std::cout << "Starting at " << us;
    do {
        quant++;
    } while (us-- != 0);
    std::cout << ", we would loop " << quant << " times.\n";
    return 0;
}

Это в основном то же самое с unsigned short, и вы можете видеть, что он обрабатывает каждое отдельное значение:

Starting at 65535, we would loop 65536 times.

Замена цикла do..while в приведенном выше коде тем, что в основном делает ваш босс, приведет к бесконечному циклу. Попробуйте и посмотрите:

for (unsigned int us2 = us; us2 <= us; --us2) {
    quant++;
}