Почему оператор continue продолжает игнорировать приращение счетчика цикла в цикле while, а не в цикле for?
Почему это имеет тенденцию попадать в бесконечный цикл, если я использую continue
в while
цикл, но отлично работает в for
цикл?
Приращение счетчика цикла i++
игнорируется в while
цикл, если я использую его после continue
, но это работает, если оно находится в for
петля.
Если continue
игнорирует последующие утверждения, то почему он не игнорирует третье утверждение for
цикл, который содержит приращение счетчика i++
? Разве это не третье утверждение for
цикл после continue
а также следует игнорировать, учитывая третье утверждение for
цикл выполняется после тела цикла?
while(i<10) //causes infinite loop
{
...
continue
i++
...
}
for(i=0;i<10;i++) //works fine and exits after 10 iterations
{
...
continue
...
}
8 ответов
Так как continue
возвращается к началу цикла. С for
Постоперация i++
является неотъемлемой частью элемента управления цикла и выполняется до перезапуска тела цикла.
С while
, i++
это просто еще одно утверждение в теле цикла (ничем не отличается от a = b
), если вы пропустили continue
прежде чем вы достигнете этого.
Причина в том, что continue
оператор будет закорачивать операторы, которые следуют за ним в теле цикла. Так как вы написали while
цикл имеет оператор приращения, следующий за continue
Заявление, оно становится замкнутым. Вы можете решить эту проблему, изменив while
петля.
Многие учебники утверждают, что:
for (i = 0; i < N; ++i) {
/*...*/
}
эквивалентно:
i = 0;
while (i < N) {
/*...*/
++i;
}
Но на самом деле это действительно так:
j = 0;
while ((i = j++) < N) {
/*...*/
}
Или, чтобы быть немного более педантичным:
i = 0;
if (i < 10) do {
/*...*/
} while (++i, (i < 10));
Они более эквивалентны, так как теперь, если тело while
имеет continue
, приращение все еще происходит, так же, как в for
, Последний вариант выполняет приращение только после завершения итерации, так же, как for
(первый выполняет приращение перед итерацией, откладывая его сохранение в i
пока после итерации).
Ваш прирост i
после продолжения, поэтому он никогда не выполняется
while(i<10) //causes infinite loop
{
.........
continue
i++
......
}
В любом цикле continue перемещает выполнение обратно в начало цикла, не выполняя никаких других инструкций после оператора continue.
В этом случае определение цикла for выполняется всегда (в соответствии со стандартом C), тогда как i++; оператор НЕ выполняется, потому что он приходит ПОСЛЕ оператора продолжения.
Цикл for содержит операторы условия и приращение, поэтому, когда условие выполнено, выполняется выполнение оператора внутри цикла for, но если записать оператор continue, он снова достигнет первой строки цикла for, то есть приращение и проверка оператора условия, если выполнятся чем снова приходит на исполнение. Для цикла while он просто проверяет оператор условия, а если условие выполнено, он выполняет инструкции в цикле while. поэтому continue не будет выполнять ни одной строки после него. Следовательно, ваше условие выполняется каждый раз и выполняется бесконечный цикл.
continue
обходит остальную часть блока и начинается снова в верхней части блока, если выполняется условие цикла.
Следующий вопрос: "Что мне тогда делать?" Есть два ответа, которые я могу придумать.
Пример:
void foo ()
{
size_t i = 0;
do
{
/*...*/
if ( /*...*/ )
{
/*...*/
continue;
}
/*...*/
i++;
} while ( /* loop conditional */ );
}
Решение № 1: увеличение вручную
void foo ()
{
size_t i = 0;
do
{
/*...*/
if ( /*...*/ )
{
/*...*/
i++;
continue;
}
/*...*/
i++;
} while ( /* loop conditional */ );
}
Решение № 2: Уникально действительное применение goto
*
void foo ()
{
size_t i = 0;
do
{
/*...*/
if ( /*...*/ )
{
/*...*/
goto foo_next;
}
/*...*/
foo_next:
i++;
} while ( /* loop conditional */ );
}
goto
допустимо в этом случае, потому что увеличение в двух местах технически одна и та же инструкция. Это решение особенно актуально, когда переменные, изменяющиеся на одну итерацию, являются более сложными; например, установка нескольких переменных или изменение значения с помощью уравнения или функции.
В случае одного заявления о приращении или уменьшении Решение № 1 может оказаться выгодным; тем не менее, следует отметить, что: если код изменяется после такой реализации, следует помнить об обновлении обоих экземпляров оператора (которые могут быть подвержены ошибкам, особенно если изменения происходят после длительного периода времени **). Поэтому я настоятельно рекомендую Решение № 2.
* Некоторые считают, что любое использование goto
плохая практика Я рекомендую вам решить для себя, и оставить вам это: Google для "C Goto Bad"
** Комментарий, напоминающий об этой необходимости, может быть достаточным, но - если мой совет был соблюден - переменные, изменяющиеся на одну итерацию, ограничиваются одним утверждением. И я цитирую:
Никогда нет причин комментировать одну строку
Линус Торвальдс (источник: http://yarchive.net/comp/linux/coding_style.html)
continue
оператор переходит к концу операторов в текущей итерации цикла, т. е. пропускает выполнение операторов в текущей итерации и переходит к следующей итерации цикла.
С while
петли, continue
оператор заставляет управление достигать конца операторов (включая оператор приращения), таким образом, цикл продолжается вечно.
С for
петли, continue
оператор переходит к элементу управления до конца оператора и выполняет оператор инкремента (In for
цикл, оператор приращения считается отдельным от операторов, записанных в теле цикла).