Jak umieścić dwa polecenia increment w pętli C++ 'for'?

Chciałbym zwiększyć dwie zmienne w warunku for-loop zamiast jednej.

Więc coś w stylu:

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

Jaka jest składnia tego?

Author: Peter Mortensen, 2009-08-05

8 answers

Powszechnym idiomem jest użycie operatora przecinka , który ocenia oba operandy i zwraca drugi operand. Tak więc:

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

Ale czy to naprawdę operator przecinka?

Po napisaniu tego komentator zasugerował, że w rzeczywistości był to jakiś specjalny cukier składniowy w instrukcji for, a nie operator przecinka. Sprawdziłem to w GCC w następujący sposób:

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);
}

Spodziewałem się, że x odbierze oryginalną wartość a, więc powinno wyświetlać 5,6,7.. dla x. co mam czy to

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

Jednak, jeśli połączyłem wyrażenie, aby zmusić parser do naprawdę zobaczenia operatora przecinka, dostaję to

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

Początkowo myślałem, że to pokazuje, że w ogóle nie zachowuje się jak operator przecinka, ale jak się okazuje, jest to po prostu problem z pierwszeństwem-operator przecinka ma najniższy możliwy pierwszeństwo , więc wyrażenie x=i++, a++ jest efektywnie przetwarzane jako (x = i++),a++

Dzięki za wszystkie komentarze, to była ciekawa nauka doświadczenie, a używam C od wielu lat!

 157
Author: Paul Dixon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-05 11:21:43

Spróbuj tego

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);
 56
Author: yeyeyerman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-05 09:42:21

Staraj się tego nie robić!

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

AV art. 199
Wyrażenie przyrostowe w pętli for nie wykona żadnej akcji innej niż zmiana pojedynczego parametr loop do następnej wartości pętli.

Uzasadnienie: Czytelność.

 6
Author: squelart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-22 00:54:21
for (int i = 0; i != 5; ++i, ++j) 
    do_something(i, j);
 3
Author: malay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-17 12:58:10

Przyszedłem tutaj, aby przypomnieć sobie, jak zakodować drugi indeks do klauzuli inkrementacji pętli FOR, co wiedziałem, że można to zrobić głównie z obserwacji w próbce, którą włączyłem do innego projektu, który napisany jest w C++.

Dzisiaj pracuję w C#, ale czułem się pewien, że będzie on przestrzegał tych samych zasad w tym zakresie, ponieważ deklaracja FOR jest jedną z najstarszych struktur sterujących w całym programowaniu. Na szczęście spędziłem ostatnio kilka dni dokładnie dokumentując zachowanie pętli FOR w jednym z moich starszych programów C i szybko zdałem sobie sprawę, że te badania prowadziły lekcje, które dotyczyły dzisiejszego problemu C#, w szczególności zachowania drugiej zmiennej indeksowej.

Dla nieostrożnych, poniżej jest podsumowanie moich obserwacji. Wszystko, co dzisiaj widziałem, poprzez uważne obserwowanie zmiennych w oknie Locals, potwierdziło moje oczekiwanie, że instrukcja C # FOR zachowuje się dokładnie tak, jak instrukcja C lub C++ FOR.

  1. Pierwszy raz a dla loop wykonuje, klauzula inkrementacji (trzecia z trzech) jest pomijana. W Visual C i C++, inkrement jest generowany jako trzy instrukcje maszynowe w środku bloku, który implementuje pętlę, tak że początkowe Przejście uruchamia kod inicjalizacji tylko raz, a następnie przeskakuje nad blokiem inkrementacji, aby wykonać test zakończenia. Implementuje to funkcję, że pętla FOR wykonuje zero lub więcej razy, w zależności od stanu jej zmiennych indeksowych i granicznych.
  2. Jeśli ciało pętla jest wykonywana, jej ostatnia instrukcja jest skokiem do pierwszej z trzech instrukcji przyrostu, które zostały pominięte przez pierwszą iterację. Po ich wykonaniu, Kontrola spada naturalnie do kodu testowego limitu, który implementuje środkową klauzulę. Wynik tego testu określa, czy ciało pętli FOR wykonuje, czy też kontrola przenosi się do następnej instrukcji po skoku na dole jej zakresu.
  3. ponieważ sterowanie przenosi się z dołu bloku pętli FOR NA blok inkrementacji, zmienna indeksu jest inkrementowana przed wykonaniem testu. To zachowanie nie tylko wyjaśnia, dlaczego musisz kodować klauzule graniczne w sposób, w jaki się nauczyłeś, ale wpływa na każdy dodatkowy przyrost, który dodajesz, za pomocą operatora przecinka, ponieważ staje się częścią trzeciej klauzuli. W związku z tym nie zmienia się ona w pierwszej iteracji, lecz w ostatniej iteracji, która nigdy nie wykonuje ciała.

Jeśli któraś ze zmiennych indeksu pozostanie w obszarze po zakończeniu pętli, ich wartość będzie o jedną wyższą od progu, który zatrzymuje pętlę, w przypadku zmiennej true index. Podobnie, jeśli, na przykład, druga zmienna jest zainicjalizowana na zero przed wprowadzeniem pętli, jej wartość na końcu będzie licznikiem iteracji, zakładając, że jest to przyrost ( ++ ), a nie spadek, i że nic w ciele pętli nie zmienia jej wartości.

 2
Author: David A. Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-10 05:01:42

Zgadzam się ze squelartem. Zwiększenie wartości dwóch zmiennych jest podatne na błędy, zwłaszcza jeśli testujesz tylko jedną z nich.

Jest to czytelny sposób, aby to zrobić:

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

For pętle są przeznaczone dla przypadków, w których pętla działa na jednej zmiennej rosnącej/malejącej. Dla każdej innej zmiennej zmień ją w pętli.

Jeśli chcesz, aby j było powiązane z i, Dlaczego nie pozostawić oryginalnej zmiennej tak, jak jest i dodać i?

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

Jeśli twoja logika jest bardziej złożona (np. potrzebujesz aby faktycznie monitorować więcej niż jedną zmienną), użyłbym pętli while.

 1
Author: Ran Halprin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-28 17:15:46
int main(){
    int i=0;
    int a=0;
    for(i;i<5;i++,a++){
        printf("%d %d\n",a,i);
    } 
}
 0
Author: Arkaitz Jimenez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-05 09:43:01

Użyj Matematyki. Jeśli te dwie operacje matematycznie zależą od iteracji pętli, dlaczego nie zrobić matematyki?

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

Lub, dokładniej odnosząc się do przykładu OP:

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

Szczególnie jeśli przechodzisz do funkcji przez wartość, powinieneś otrzymać coś, co robi dokładnie to, co chcesz.

 0
Author: xaviersjs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-19 18:54:21