Я сейчас отлаживаю какой-то код и наткнулся на эту строку:
for (std::size_t j = M; j <= M; --j)
(Написано моим начальником, который в отпуске.)
Мне это кажется действительно странным.
Что оно делает? На мой взгляд, это похоже на бесконечный цикл.
size_t
беззнаковый, поэтому гарантируется возврат к максимальному значению при попытке отказаться от нуля, завершая цикл. Тем не менее, все еще ужасный код.M
это максимальное значениеsize_t
? Вы все еще думаете, что это умно?#define TRUE FALSE
и поехать в отпуск.Ответы:
std::size_t
Стандарт C ++ гарантирует, что этоunsigned
тип. И если вы уменьшитеunsigned
тип с 0, стандарт гарантирует, что результатом этого будет наибольшее значение для этого типа.Это значение всегда больше или равно
M
1 цикла поэтому цикл завершается.Таким образом,
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()
.источник
std::numeric_limits<std::size_t>::max()
тоM + 1
будет равен нулю, иfor (std::size_t j = M + 1; j --> 0; )
цикл не будет цикл на всех.size_t
это 64 бита, потребуется несколько сотен лет, чтобы увидеть некорректное поведение в граничном случае. (За исключением случаев, когда оптимизатор может избавиться от цикла.)Вероятно, ваш начальник пытался вести обратный отсчет от
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++; }
источник
0
. А почему бы и нетfor(size_t j = M; j-- != 0; )
?numeric_limits<size_t>::max()
.M == 0
приведет к элементу-не обрабатываются, поэтому он делает есть крайний случай. Использование моегоdo..while
метода постпроверки полностью исключает крайний случай. Если вы попробуете с помощьюM == 1
, вы увидите, что он выполняет и 1, и 0. Точно так же начните его сmax_size_t
(что бы это ни было), и он успешно запустится в этой точке, а затем уменьшится до нуля включительно.exitif
», а именно{ j =M; for(;;){ f(j); if( j == 0 )break; j -= 1; } }
. Может даже потребоваться заменить наbreak
a,goto
если вещи будут вложенными, поскольку C не имеет именованных циклов. Если «структурированный» означает «восприимчивый к рассуждениям о фрагментах», он структурирован (макет помогает!), А также, если он означает «восприимчивый к формальной проверке посредством рассуждений о предварительных и пост-условиях». Хотя в этом случаеj--
работает, стиль exitif может быть оправдан, когда требуется более сложный переход.