Запятая не является исключением для циклов; это оператор запятой.
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
указывает операцию (например, увеличение), которая выполняется после каждой итерации.