web-dev-qa-db-fra.com

Comment mettre deux instructions d'incrémentation dans une boucle 'for' de C ++?

Je voudrais incrémenter deux variables dans une condition de boucle for- au lieu d'une.

Donc, quelque chose comme:

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

Quelle est la syntaxe pour cela?

80
Peter Smit

Un langage courant consiste à utiliser opérateur de virgule , qui évalue les deux opérandes et renvoie le deuxième opérande. Ainsi:

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

Mais est-ce vraiment un opérateur de virgule?

Après avoir écrit cela, un commentateur a suggéré qu'il s'agissait en fait d'un sucre syntaxique spécial dans l'instruction for et non d'un opérateur de virgule. J'ai vérifié cela dans GCC comme suit:

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

Je m'attendais à ce que x récupère la valeur initiale de a, il aurait donc dû afficher 5,6,7 .. pour x. Ce que j'ai eu était ceci

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

Cependant, si je met l'expression entre crochets pour forcer l'analyseur à vraiment voir un opérateur de virgule, je reçois ceci

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

Au départ, je pensais que cela montrait que cela ne se comportait pas du tout comme un opérateur de virgule, mais il s’avère que c’est tout simplement un problème de priorité - l’opérateur de virgule a le le plus bas précédent possible , ainsi l'expression x = i ++, un ++ est effectivement analysé comme (x = i ++), un ++

Merci pour tous les commentaires, ce fut une expérience d'apprentissage intéressante et j'utilise C depuis de nombreuses années!

139
Paul Dixon

Essaye ça

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

Essayez de ne pas le faire!

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

Règle AV 199
L’expression d’incrémentation dans une boucle for n’exécutera aucune action autre que celle de changer un paramètre de boucle unique à la valeur suivante de la boucle.

Justification: lisibilité.

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

Je suis venu ici pour me rappeler comment coder un deuxième index dans la clause d'incrémentation d'une boucle FOR, ce que je savais pouvoir être fait principalement en l'observant dans un exemple que j'ai incorporé à un autre projet, celui écrit en C++.

Aujourd'hui, je travaille en C #, mais je suis convaincu qu'il obéira aux mêmes règles à cet égard, car l'instruction FOR est l'une des plus anciennes structures de contrôle de toute la programmation. Heureusement, j'avais récemment passé plusieurs jours à documenter avec précision le comportement d'une boucle FOR dans l'un de mes anciens programmes C et j'ai vite compris que ces études contenaient des leçons qui s'appliquent au problème C # actuel, en particulier au comportement de la deuxième variable d'index .

Pour les imprudents, voici un résumé de mes observations. Tout ce que j'ai vu aujourd'hui, en observant attentivement les variables de la fenêtre Locals, a confirmé mon attente selon laquelle une instruction C # FOR se comporte exactement comme une instruction C ou C++ FOR.

  1. La première fois qu'une boucle FOR est exécutée, la clause d'incrémentation (la troisième de ses trois) est ignorée. Dans Visual C et C++, l'incrément est généré sous la forme de trois instructions machine au milieu du bloc qui implémente la boucle, de sorte que le passage initial n'exécute le code d'initialisation qu'une seule fois, puis saute par-dessus le bloc d'incrément pour exécuter le test de terminaison. Cela implémente la fonction qu'une boucle FOR exécute zéro ou plusieurs fois, en fonction de l'état de son index et de ses variables limites.
  2. Si le corps de la boucle s'exécute, sa dernière instruction est un saut vers la première des trois instructions d'incrémentation ignorées par la première itération. Après leur exécution, le contrôle tombe naturellement dans le code de test limite qui implémente la clause middle. Le résultat de ce test détermine si le corps de la boucle FOR est exécuté ou si le contrôle est transféré à l'instruction suivante après le saut situé au bas de sa portée.
  3. Comme la commande passe du bas du bloc de boucle FOR au bloc d’incrément, la variable d’index est incrémentée avant l’exécution du test. Non seulement ce comportement explique pourquoi vous devez coder vos clauses limit comme vous l'avez appris, mais il affecte également tout incrément secondaire que vous ajoutez via l'opérateur virgule, car il fait partie de la troisième clause. Par conséquent, il n'est pas modifié à la première itération, mais à la dernière itération, qui n'exécute jamais le corps.

Si l'une de vos variables d'index reste dans l'étendue à la fin de la boucle, sa valeur sera supérieure au seuil qui arrête la boucle, dans le cas de la vraie variable d'index. De même, si, par exemple, la deuxième variable est initialisée à zéro avant la boucle, sa valeur à la fin sera le compte d’itérations, en supposant qu’il s’agit d’un incrément (++), pas d’un décrément, et que rien dans le corps de la boucle change de valeur.

2
David A. Gray

Je suis d'accord avec squelart. L'incrémentation de deux variables est sujette aux bogues, surtout si vous ne testez que l'une d'entre elles.

C'est le moyen lisible de faire ceci:

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

Les boucles For sont destinées aux cas où votre boucle s'exécute sur une variable croissante/décroissante. Pour toute autre variable, changez-la dans la boucle.

Si vous avez besoin que j soit lié à i, pourquoi ne pas laisser la variable originale telle quelle et ajouter i?

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

Si votre logique est plus complexe (par exemple, vous devez surveiller plusieurs variables), utilisez une boucle while.

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

Utilisez les mathématiques. Si les deux opérations dépendent mathématiquement de l'itération de la boucle, pourquoi ne pas faire le calcul?

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

Ou, plus spécifiquement, en vous référant à l'exemple du PO:

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

Surtout si vous passez dans une fonction par valeur, alors vous devriez obtenir quelque chose qui fait exactement ce que vous voulez.

0
xaviersjs