Come inserisco due istruzioni di incremento in un ciclo "for" di C ++?


93

Vorrei incrementare due variabili in una forcondizione -loop invece di una.

Quindi qualcosa come:

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

Qual è la sintassi per questo?

Risposte:


154

Un linguaggio comune consiste nell'usare l' operatore virgola che valuta entrambi gli operandi e restituisce il secondo operando. Quindi:

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

Ma è davvero un operatore virgola?

Dopo aver scritto questo, un commentatore ha suggerito che in realtà fosse uno speciale zucchero sintattico nell'istruzione for, e non un operatore virgola. L'ho verificato in GCC come segue:

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

Mi aspettavo che x prendesse il valore originale di a, quindi avrebbe dovuto visualizzare 5,6,7 .. per x. Quello che ho ottenuto è stato questo

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

Tuttavia, se ho messo tra parentesi l'espressione per forzare il parser a vedere davvero un operatore virgola, ottengo questo

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

Inizialmente pensavo che questo mostrasse che non si comportava affatto come un operatore virgola, ma a quanto pare, questo è semplicemente un problema di precedenza: l'operatore virgola ha la precedenza più bassa possibile , quindi l'espressione x = i ++, a ++ è effettivamente analizzato come (x = i ++), a ++

Grazie per tutti i commenti, è stata un'esperienza di apprendimento interessante e uso C da molti anni!


1
Ho letto più volte che la virgola nella prima o terza parte di un ciclo for non è l'operatore virgola, ma solo un separatore virgola. (Tuttavia, non riesco a trovare una fonte ufficiale per questo, dal momento che sono particolarmente pessimo
nell'analisi dello

All'inizio pensavo che fossi errato, ma ho scritto del codice di prova e hai ragione - non si comporta come un operatore di virgola. Modificherò la mia risposta!
Paul Dixon

19
Si tratta di un operatore virgola in quel contesto. Il motivo per cui non ottieni ciò che ti aspetti è che l'operatore di comando ha una precedenza inferiore rispetto all'operatore di assegnazione, quindi senza le parentesi viene analizzato come (x = i ++), j ++.
caf il

6
È un operatore virgola. L'assegnazione è più vincolante dell'operatore virgola, quindi x = i ++, a ++ viene analizzato (x = i ++), a ++ e non x = (i ++, a ++). Questa caratteristica è usata impropriamente da alcune librerie in modo che v = 1,2,3; fa le cose intuitive, ma solo perché v = 1 restituisce un oggetto proxy per il quale l'operatore virgola sovraccarico fa un'appendice.
AProgrammer

3
Ok. Dalla sezione 6.5.3 di open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdf , l'ultima parte è una "espressione". (Sebbene 1.6 # 2 definisca un "elenco di espressioni" come un "elenco di espressioni separate da virgole", questo costrutto non appare in 6.5.3.). Ciò significa che quando scriviamo "++ i, ++ j" deve essere un'espressione in un a sé stante, e quindi "," deve essere l'operatore virgola (5.18). (Questo non è un "elenco di inizializzatori" o un "elenco di argomenti per funzioni", che sono esempi in cui "alla virgola viene assegnato un significato speciale", come dice 5.18 # 2). Lo trovo un po 'confuso però.
Daniel Daranas

55

Prova questo

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

17
+1 Puoi anche dichiarare j nella prima parte. for (int i = 0, j = 0; i! = 5; ++ i, ++ j) {...}
Daniel Daranas

1
+1 Come nota a margine, questa stessa sintassi funziona in C # (sono arrivato qui da una ricerca su Google per "C # for loop incrament 2 counters", quindi ho pensato di menzionarlo).
CodingWithSpike

@CodingWithSpike: Bene, in C # la virgola è speciale, in realtà non è legale che vi appaia un'espressione di operatore virgola. Esempio di utilizzo legale dell'operatore virgola in C ++, ma rifiutato da C #:for( ; ; ((++i), (++j)) )
Ben Voigt

@BenVoigt non ha nulla a che fare con la virgola. Anche questo non è legale C #: for(int i = 0; i != 5; (++i)) {le parentesi extra inducono il compilatore a pensare che non sia più un'operazione di "incremento".
CodingWithSpike

@CodingWithSpike: è vero, ma le parentesi cambiano anche il modo in cui C # vede la virgola e impedisce il significato speciale all'interno dell'azione for.
Ben Voigt

6

Cerca di non farlo!

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

Regola AV 199
L'espressione di incremento in un ciclo for non eseguirà alcuna azione se non quella di modificare un singolo parametro del ciclo al valore successivo per il ciclo.

Motivazione: leggibilità.


4
È vero, ma per essere onesti sono abbastanza sicuro che lo standard di regole è stato scritto per il software incorporato in un jet da combattimento, non per il programma C (++) di varietà da giardino. Detto questo, probabilmente è una buona abitudine di leggibilità in cui entrare, e chissà, forse progetterai un software F-35 e sarà un'abitudine in meno da rompere.
galois

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

2

Sono venuto qui per ricordare a me stesso come codificare un secondo indice nella clausola di incremento di un ciclo FOR, che sapevo poteva essere fatto principalmente osservandolo in un campione che ho incorporato in un altro progetto, quello scritto in C ++.

Oggi lavoro in C #, ma ero sicuro che avrebbe obbedito alle stesse regole a questo proposito, poiché l'istruzione FOR è una delle strutture di controllo più antiche in tutta la programmazione. Per fortuna, di recente avevo trascorso diversi giorni a documentare con precisione il comportamento di un ciclo FOR in uno dei miei vecchi programmi in C, e mi sono subito reso conto che quegli studi tenevano lezioni che si applicavano al problema C # di oggi, in particolare al comportamento della seconda variabile di indice .

Per gli incauti, di seguito è riportato un riassunto delle mie osservazioni. Tutto quello che ho visto accadere oggi, osservando attentamente le variabili nella finestra Locali, ha confermato la mia aspettativa che un'istruzione C # FOR si comporti esattamente come un'istruzione C o C ++ FOR.

  1. La prima volta che viene eseguito un ciclo FOR, la clausola di incremento (la terza delle sue tre) viene ignorata. In Visual C e C ++, l'incremento viene generato come tre istruzioni macchina nel mezzo del blocco che implementa il ciclo, in modo che il passaggio iniziale esegua il codice di inizializzazione una sola volta, quindi salti sul blocco di incremento per eseguire il test di terminazione. Ciò implementa la funzionalità che un ciclo FOR esegue zero o più volte, a seconda dello stato del suo indice e delle sue variabili limite.
  2. Se il corpo del ciclo viene eseguito, la sua ultima istruzione è un salto alla prima delle tre istruzioni di incremento che sono state ignorate dalla prima iterazione. Dopo che questi sono stati eseguiti, il controllo ricade naturalmente nel codice di test limite che implementa la clausola centrale. Il risultato di quel test determina se il corpo del ciclo FOR viene eseguito o se il controllo si trasferisce all'istruzione successiva oltre il salto in fondo al suo ambito.
  3. Poiché il controllo viene trasferito dalla parte inferiore del blocco del ciclo FOR al blocco di incremento, la variabile indice viene incrementata prima dell'esecuzione del test. Questo comportamento non solo spiega perché devi codificare le tue clausole limite nel modo in cui hai imparato, ma influisce su qualsiasi incremento secondario che aggiungi, tramite l'operatore virgola, perché diventa parte della terza clausola. Quindi, non viene modificato nella prima iterazione, ma è nell'ultima iterazione, che non esegue mai il corpo.

Se una delle variabili di indice rimane nell'ambito quando il ciclo termina, il loro valore sarà superiore di uno alla soglia che arresta il ciclo, nel caso della variabile di indice vera. Allo stesso modo, se, ad esempio, la seconda variabile viene inizializzata a zero prima che il ciclo venga inserito, il suo valore alla fine sarà il conteggio delle iterazioni, assumendo che sia un incremento (++), non un decremento, e che nulla in il corpo del ciclo cambia il suo valore.


1

Sono d'accordo con squelart. L'incremento di due variabili è soggetto a bug, specialmente se si prova solo per una di esse.

Questo è il modo leggibile per farlo:

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

Fori cicli sono pensati per i casi in cui il tuo ciclo viene eseguito su una variabile crescente / decrescente. Per qualsiasi altra variabile, modificala nel ciclo.

Se hai bisogno jdi essere legato a i, perché non lasciare la variabile originale così com'è e aggiungere i?

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

Se la tua logica è più complessa (ad esempio, devi effettivamente monitorare più di una variabile), utilizzerei un whileciclo.


2
Nel primo esempio, j viene incrementato ancora una volta di i! Che ne dici di un iteratore in cui è necessario eseguire alcune azioni per i primi x passaggi? (E la raccolta è sempre abbastanza lunga) Puoi far salire l'iteratore ogni iterazione, ma è molto più pulito imho.
Peter Smit il

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

1
Qual è il punto di non fare ie alocalizzare il loop?
sbi

2
Nessuno, mostra solo come eseguire entrambi gli incrementi nel for, è solo un esempio della sytnax
Arkaitz Jimenez il

0

Usa la matematica. Se le due operazioni dipendono matematicamente dall'iterazione del ciclo, perché non fare i conti?

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

O, più specificamente, facendo riferimento all'esempio del PO:

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

Soprattutto se stai passando a una funzione in base al valore, dovresti ottenere qualcosa che fa esattamente quello che vuoi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.