Как работают запятые в инициализации и приращения частей цикла for-loop?

Я столкнулся с циклом for-loop, который выглядит следующим образом:

for ( argc--, argv++; argc > 0; argc--, argv++ )

Как это работает? Обычно цикл for выглядит следующим образом:

for (initialization; condition; increment) {/*body of the loop*/}

Но это не содержит никаких запятых - что означают запятые и делают?

Ответ 1

В стандарте C (6.8.5.3 Оператор for) оператор for представлен в следующем виде

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

и в соответствии с пунктом-1 написано

Если предложение-1 является выражением, оно оценивается как выражение void перед первой оценкой управляющего выражения

В этом для оператора

for ( argc--, argv++; argc > 0; argc--, argv++ )  

clause-1 - выражение argc--, argv++ на основе оператора запятой. Из C-стандарта (6.5.17 Comma-оператора)

2 Левый операнд оператора запятой оценивается как пустота выражение; между его оценкой и точкой правого операнда. Затем оценивается правый операнд; результат имеет свой тип и значение.

Единственная особенность заключается в том, что результат оператора не используется в инструкции for. Выражение используется для его побочных эффектов.

Обычно первым параметром, переданным запущенной программе, является его имя. Выражение в разделе-1 пропускает этот первый параметр.

Сравните выходные данные этих двух программ. Предположим, что пользователь указал параметры командной строки

first second third

Вывод программы этой программы

#include <stdio.h>

int main( int argc, char * argv[] )
{
    for ( argc--, argv++; argc > 0; argc--, argv++ )
    {
        puts( *argv );
    }        

    return 0;
}

является

first 
second 
third

и выход программы этой программы, когда предложение-1 пуст (ни выражение, ни объявление)

#include <stdio.h>

int main( int argc, char * argv[] )
{
    for ( /*argc--, argv++*/; argc > 0; argc--, argv++ )
    {
        puts( *argv );
    }        

    return 0;
}

является

./prog.exe
first 
second 
third

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

#include <stdio.h>

int main( int argc, char * argv[] )
{
    while ( argv++, --argc > 0 )
    {
        puts( *argv );
    }        

    return 0;
}

Результат будет таким же, как в первой демонстрационной программе

first 
second 
third

Здесь в инструкции while используется также оператор запятой. Разница в том, что в этом случае значение оператора запятой используется как значение условия.

Обратите внимание на то, что выражение-3 также представляет собой выражение с оператором запятой.

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

Ответ 2

Как уже было сказано в ответах many, это оператор запятой, поэтому

argc--, argv++

- это выражение one.

Оператор запятой оценивает обе стороны, сначала левую, затем правую. В результате получается правая сторона. Таким образом, вы могли бы написать некоторые странные вещи, такие как

int a = (x += 5, x + 2);

это добавит 5 к x, прежде чем назначить результат x + 2 на a. Такой код запутан, и его следует избегать. Но это демонстрирует важное свойство оператора запятой:

Он действует как точка последовательности. С приведенным выше кодом у вас есть гарантия того, что 5 уже добавлено в x (значение x действительно изменилось), прежде чем x + 2 будет оцениваться.

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

Чтобы выяснить, почему последовательность может быть важной (это не в вашем примере, потому что побочные эффекты не зависят друг от друга), посмотрите на этот (искусственный) пример:

int i, j;
for (i = j = 0; i < 10; ++i, j+=i)
{
    printf("%d\n", j);
}

Если бы оператор-запятая не вводил здесь точку последовательности, вы бы не знали, будет ли j+=i добавлять инкрементированный i или неинкрементный.

Ответ 3

Для множественной инициализации и множественного обновления/инкрементальности мы используем comma operator(,). Мы разделяем каждый экземпляр с помощью comma(,).
В этом случае, когда введен цикл for, выполняются выражения argc-- и argv++ в части инициализации. С этого момента каждый раз, когда цикл повторяется, выполняются выражения argc-- и argv++ в инкрементной части.

Ответ 4

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

for(
    (argc--, argv++);  // Expression 1
    argc > 0;          // Expression 2
    (argc--, argv++)   // Expression 3
  )  

Есть только три выражения (argc--, argv++), argc > 0 и (argc--, argv++).
Выражение 1 не обязательно должно быть объявлением, оно может быть любым допустимым выражением или даже может быть опущено

for(;expression2; expression3)

или все выражения могут быть опущены

for(;;)  

В данном цикле (argc--, argv++) используется как первое выражение для обновления переменных argc и argv (argc будет уменьшаться на 1, а указатель argv будет увеличен на 1). Как только побочный эффект этих переменных будет выполнен, программа войдет в тело цикла после проверки argc > 0 для true. Это то, что происходит, когда вы делаете

for( i = 1; i < 10; i++)

i = 1 обновить i до 1, а затем проверить состояние. Это обновление i выполняется только один раз, а затем для остальных оно обновляется выражением i++.

Ответ 5

for ( argc--, argv++; argc > 0; argc--, argv++ )  
{ ... }

Выполняется ли следующее:

  • Выполнить "Инициализация": Decrement argc и increment argv
  • Проверьте, если argv > 0, если это не так, то выйдите из цикла
  • Выполнить { ... }
  • Выполнение части "Обновление": Уменьшение argc и приращение argv
  • Перейдите к шагу 2. выше

Поскольку "Инициализация" и "Обновление" одинаковы, это также можно записать как

while (argc--, argv++, argc > 0)
{ ... }

Это выражение

(argc--, argv++, argc > 0)

состоит из трех подвыражений, разделенных запятой.

Эти подвыражения выполняются слева направо.

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

Ответ 6

argv владеет аргументами командной строки. Однако самое первое - это название программы.

Итак, цикл начинается с argv[1] и обрабатывает все аргументы, заданные командной строкой, без обработки имени программы

Ответ 7

for ( argc--, argv++; argc > 0; argc--, argv++ ) означает, что цикл начинается со значений argc, argv, установленных соответственно минус 1 и плюс 1 их начальных значений. Каждая итерация будет уменьшаться и увеличивать их значения, и она остановится, как только argc достигнет 0 (что означает, что все входные аргументы были прочитаны).

Ответ 8

for ( argc--, argv++; argc > 0; argc--, argv++ )

можно читать как

for ( (argc--), argv++; argc > 0; (argc--), argv++ )

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

Ответ 9

Параметр инициализации в цикле for не означает только инициализацию переменной с определенным значением.

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

Надеюсь, это поможет!