Как поместить два инкрементных оператора в цикл C ++ для for? - PullRequest
77 голосов
/ 05 августа 2009

Я хотел бы увеличить две переменные в условии for -loop вместо одной.

Так что-то вроде:

for (int i = 0; i != 5; ++i and ++j) 
    do_something(i, j);

Какой синтаксис для этого?

Ответы [ 8 ]

136 голосов
/ 05 августа 2009

Распространенной идиомой является использование оператора с запятыми , который вычисляет оба операнда и возвращает второй операнд. Таким образом:

for(int i = 0; i != 5; ++i,++j) 
    do_something(i,j);

Но действительно ли это оператор запятой?

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

int i=0;
int a=5;
int x=0;

for(i; i<5; x=i++,a++){
    printf("i=%d a=%d x=%d\n",i,a,x);
}

Я ожидал, что x выберет исходное значение a, поэтому он должен был отобразить 5,6,7 .. для x. Я получил это

i=0 a=5 x=0
i=1 a=6 x=0
i=2 a=7 x=1
i=3 a=8 x=2
i=4 a=9 x=3

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

int main(){
    int i=0;
    int a=5;
    int x=0;

    for(i=0; i<5; x=(i++,a++)){
        printf("i=%d a=%d x=%d\n",i,a,x);
    }
}

i=0 a=5 x=0
i=1 a=6 x=5
i=2 a=7 x=6
i=3 a=8 x=7
i=4 a=9 x=8

Первоначально я думал, что это показывает, что он вообще не работает как оператор запятой, но, как оказалось, это просто проблема приоритета - оператор запятой имеет минимально возможный приоритет , поэтому выражение x = i ++, a ++ эффективно анализируется как (x = i ++), a ++

Спасибо за все комментарии, это был интересный опыт обучения, и я использую C много лет!

53 голосов
/ 05 августа 2009

Попробуйте это

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);
5 голосов
/ 05 августа 2009

Постарайтесь не делать этого!

С http://www.research.att.com/~bs/JSF-AV-rules.pdf:

AV Rule 199
Выражение приращения в цикле for не будет выполнять никаких действий, кроме изменения одного параметр цикла до следующего значения для цикла.

Обоснование: удобочитаемость.

3 голосов
/ 05 августа 2009
for (int i = 0; i != 5; ++i, ++j) 
    do_something(i, j);
2 голосов
/ 10 июня 2016

Я пришел сюда, чтобы напомнить себе, как кодировать второй индекс в предложении приращения цикла FOR, что, как я знал, можно было сделать главным образом из наблюдения за ним в образце, который я включил в другой проект, написанный на C ++.

Сегодня я работаю в C #, но я был уверен, что он будет подчиняться тем же правилам в этом отношении, поскольку оператор FOR является одной из самых старых управляющих структур во всем программировании. К счастью, я недавно провел несколько дней, чтобы точно документировать поведение цикла FOR в одной из моих старых программ на C, и я быстро понял, что в этих исследованиях были извлечены уроки, относящиеся к сегодняшней проблеме C #, в частности к поведению второй индексной переменной. .

Для неосторожных ниже приводится краткое изложение моих наблюдений. Все, что я видел сегодня, внимательно наблюдая за переменными в окне Locals, подтвердило мои ожидания, что оператор C # FOR ведет себя точно так же, как оператор C или C ++ FOR.

  1. При первом выполнении цикла FOR предложение приращения (третье из трех) пропускается. В Visual C и C ++ приращение генерируется в виде трех машинных инструкций в середине блока, который реализует цикл, так что начальный этап запускает код инициализации только один раз, а затем перепрыгивает через блок приращения, чтобы выполнить тест завершения. Это реализует функцию, что цикл FOR выполняется ноль или более раз, в зависимости от состояния его индекса и предельных переменных.
  2. Если тело цикла выполняется, его последний оператор является переходом к первой из трех инструкций приращения, которые были пропущены при первой итерации. После их выполнения управление естественным образом попадает в код проверки предела, который реализует предложение middle. Результат этого теста определяет, выполняется ли тело цикла FOR или управление переходит к следующей инструкции после перехода в нижней части области видимости.
  3. Поскольку управление переходит из нижней части блока цикла FOR в блок приращения, индексная переменная увеличивается до выполнения теста. Это поведение не только объясняет, почему вы должны кодировать предельные предложения, как вы узнали, но и влияет на любое добавляемое вами дополнительное приращение через оператор запятой, поскольку оно становится частью третьего предложения. Следовательно, он не изменяется на первой итерации, но на последней итерации, которая никогда не выполняет тело.

Если одна из ваших индексных переменных останется в области видимости после окончания цикла, их значение будет на один уровень выше порогового значения, которое останавливает цикл, в случае истинной индексной переменной. Аналогично, если, например, вторая переменная инициализируется в ноль до того, как вводится цикл, ее значением в конце будет счетчик итераций, при условии, что это приращение (++), а не уменьшение, и что ничего в тело цикла меняет свое значение.

1 голос
/ 05 августа 2009

Я согласен с squelart. Увеличение двух переменных подвержено ошибкам, особенно если вы проверяете только одну из них.

Это читабельный способ сделать это:

for(int i = 0; i < 5; ++i) {
    ++j;
    do_something(i, j);
}
Циклы

For предназначены для случаев, когда ваш цикл работает с одной возрастающей / убывающей переменной. Для любой другой переменной измените ее в цикле.

Если вам нужно привязать j к i, почему бы не оставить исходную переменную как есть и добавить i?

for(int i = 0; i < 5; ++i) {
    do_something(i,a+i);
}

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

0 голосов
/ 11 июня 2015

Используйте математику. Если две операции математически зависят от итерации цикла, почему бы не сделать математические вычисления?

int i, j;//That have some meaningful values in them?
for( int counter = 0; counter < count_max; ++counter )
    do_something (counter+i, counter+j);

Или, более конкретно, ссылаясь на пример ОП:

for(int i = 0; i != 5; ++i)
    do_something(i, j+i);

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

0 голосов
/ 05 августа 2009
int main(){
    int i=0;
    int a=0;
    for(i;i<5;i++,a++){
        printf("%d %d\n",a,i);
    } 
}
...