Почему оператор 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 цикл, оператор приращения считается отдельным от операторов, записанных в теле цикла).

Потому что третья часть для всегда выполняется.

Другие вопросы по тегам