Каков полный синтаксис цикла for в C? - PullRequest
59 голосов
/ 10 ноября 2008

Я видел очень странные циклы for при чтении чужого кода. Я пытался найти полное объяснение синтаксиса для цикла for в C, но это очень сложно, потому что слово "for" появляется в несвязанных предложениях, что делает поиск практически невозможным для Google.

Этот вопрос возник у меня после прочтения этой ветки , что снова вызвало у меня любопытство.

for здесь:

for(p=0;p+=(a&1)*b,a!=1;a>>=1,b<<=1);

В среднем состоянии есть запятая, разделяющая два куска кода, что делает эта запятая? Запятую справа я понимаю, так как она составляет a>>=1 и b<<=1.

Но что происходит в условиях выхода из цикла? Выходит ли он, когда p==0, a==1 или когда оба происходят?

Было бы здорово, если бы кто-нибудь мог помочь мне понять это и, возможно, указать мне направление полного описания синтаксиса for.

Ответы [ 6 ]

131 голосов
/ 10 ноября 2008

Запятая не является исключением для циклов; это оператор запятой.

x = (a, b);

сначала сделает a, затем b, затем установит для x значение b.

Синтаксис для:

for (init; condition; increment)
    ...

Что в некоторой степени (игнорируя continue и break на данный момент) эквивалентно:

init;
while (condition) {
    ...
    increment;
}

Таким образом, ваш цикл for (опять игнорируя continue и break) эквивалентен

p=0;
while (p+=(a&1)*b,a!=1) {
    ...
    a>>=1,b<<=1;
}

Что действует так, как если бы это было (опять игнорируя continue и break):

p=0; 
while (true) {
    p+=(a&1)*b;
    if (a == 1) break;
    ...
    a>>=1;
    b<<=1;
}

Две дополнительные детали цикла for, которые не были указаны в упрощенном преобразовании в цикл while:

  • Если условие опущено, оно всегда равно true (что приводит к бесконечному циклу, если только break, goto или что-то еще не прервет цикл).
  • A continue действует так, как если бы это был переход к метке непосредственно перед приращением, в отличие от continue в цикле while, который пропустил бы приращение.

Также важная деталь в операторе запятой: это точка последовательности, такая как && и || (поэтому я могу разделить ее на отдельные операторы и сохранить ее значение без изменений).


Изменения в C99

Стандарт C99 вводит пару нюансов, не упомянутых ранее в этом объяснении (что очень хорошо для C89 / C90).

Во-первых, все петли являются самостоятельными блоками. Эффективно,

for (...) { ... }

сама обернута в пару скобок

{
for (...) { ... }
}

Стандарт говорит:

ISO / IEC 9899: 1999 §6.8.5 Итерационные операторы

¶5 Оператор итерации - это блок, область действия которого является строгим подмножеством области действия его ограждающий блок. Тело цикла также является блоком, область действия которого является строгим подмножеством области действия. оператора итерации.

Это также описано в Обосновании в терминах дополнительного набора скобок.

Во-вторых, часть init в C99 может быть (одиночной) декларацией, как в

for (int i = 0; i < sizeof(something); i++) { ... }

Теперь «блок, обернутый вокруг петли», вступает в свои права; это объясняет, почему переменная i не может быть доступна вне цикла. Вы можете объявить более одной переменной, но все они должны быть одного типа:

for (int i = 0, j = sizeof(something); i < j; i++, j--) { ... }

Стандарт говорит:

ИСО / МЭК 9899: 1999 §6.8.5.3 Для заявления

Заявление

for ( clause-1 ; expression-2 ; expression-3 ) statement

ведет себя следующим образом: выражение-выражение-2 является управляющим выражением, которое оценивается перед каждым выполнением тела цикла. Выражение выражение-3 является оценивается как пустое выражение после каждого выполнения тела цикла. Если пункт-1 является декларация, область действия любых переменных, которые она объявляет, является остальной частью объявления и весь цикл, включая два других выражения; достигается в порядке исполнения до первой оценки контрольного выражения. Если предложение-1 является выражением, оно оценивается как пустое выражение перед первой оценкой управляющего выражения. 133)

И условие-1, и выражение-3 могут быть опущены. Опущенное выражение-2 заменяется на ненулевая константа.

133) Таким образом, пункт-1 определяет инициализацию для цикла, возможно, объявляя одну или несколько переменных для использования в петля; управляющее выражение expression-2 определяет оценку, выполненную перед каждой итерацией, так, что выполнение цикла продолжается до тех пор, пока выражение не сравнится равным 0; и выражение-3 указывает операцию (например, увеличение), которая выполняется после каждой итерации.

8 голосов
/ 10 ноября 2008

Запятая просто разделяет два выражения и действует в любом месте C, где допускается нормальное выражение. Они выполняются в порядке слева направо. Значение самого правого выражения является значением всего выражения.

for петли состоят из трех частей, любая из которых также может быть пустой; один (первый) выполняется в начале, а один (третий) в конце каждой итерации. Эти части обычно инициализируют и увеличивают счетчик соответственно; но они могут делать что угодно.

Вторая часть - это тест , который выполняется в начале каждого выполнения. Если тест дает false, цикл прерывается. Вот и все, что нужно.

5 голосов
/ 10 ноября 2008

Стиль C для цикла состоит из трех выражений:

for (initializer; condition; counter) statement_or_statement_block;
  • Инициализатор запускается один раз, когда начинается цикл.
  • Условие проверяется перед каждой итерацией. Цикл выполняется до тех пор, пока он оценивается как true.
  • Счетчик запускается один раз после каждой итерации.

Каждая из этих частей может быть выражением, действительным на языке, на котором вы пишете цикл. Это означает, что они могут использоваться более творчески. Все, что вы хотите сделать заранее, может попасть в инициализатор, все, что вы хотите сделать между ними, может перейти в условие или в счетчик, вплоть до точки, где цикл больше не имеет тела.

Для этого очень удобно использовать оператор запятой. Это позволяет объединять выражения в единое новое выражение. Большую часть времени он используется таким образом в цикле for, другие значения оператора запятой (например, соображения присваивания значения) играют второстепенную роль.

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

В википедии есть хорошая статья о цикле for .

2 голосов
/ 09 марта 2009

Все необязательно в цикле for. Мы можем инициализировать более одной переменной, мы можем проверить более одного условия, мы можем перебрать более одной переменной, используя оператор запятой.

Следующая for петля приведет вас в бесконечный цикл. Будьте осторожны, проверяя состояние.

for(;;) 
0 голосов
/ 14 октября 2014

цикл for - это выполнение в течение определенного времени для (;;)

синтаксис для цикла

для (;;)

OR

для (инициализатор; условие; счетчик)

например (rmv = 1; rmv <= 15; rmv ++) </p>

выполнение до 15 раз за блок

1.Первая инициализация значения, потому что начало значения

(например, rmv = 1 или rmv = 2

2.Второй оператор проверяет, является ли условие истинным или ложным, условие истинно нет. Время выполнения цикла for и условие ложно завершается для блока,

например, я = 5; я <= 10, условие истинно </p>

i=10;i<10 the condition is false terminate for block,

3. Третье увеличение или уменьшение

(например) rmv ++ или ++ rmv

0 голосов
/ 13 апреля 2010

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

Компилятор Gnu заявил об этом предупреждении, когда я поместил два теста в раздел «условия» цикла for

warning: left-hand operand of comma expression has no effect

То, что я действительно намеревался для "условия", было двумя тестами с "&&" между ними. Согласно утверждению Конрада, только условие справа от запятой может повлиять на состояние.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...