web-dev-qa-db-fra.com

Comment fonctionnent les virgules lors de l'initialisation et de l'incrémentation d'une partie d'une boucle for?

Je suis tombé sur une boucle for dans le code qui ressemble à ceci:

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

Comment ça marche? Normalement, une boucle for ressemble à ceci:

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

Mais cela ne contient aucune virgule - que signifient les virgules et qu’elles font?

12
user3552519

Dans la norme C (6.8.5.3 La déclaration for), la déclaration for est présentée sous la forme suivante

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

et selon le clause-1 il est écrit

Si la clause-1 est une expression, elle est évaluée comme une expression vide Avant la première évaluation de l'expression de contrôle.

Dans cette déclaration

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

clause-1 est l'expression argc--, argv++ basée sur l'opérateur de virgule. D'après le standard C (opérateur de virgule 6.5.17).

2 L'opérande gauche d'un opérateur de virgule est évalué comme une expression vide ; il y a un point de séquence entre son évaluation et celui de l'opérande droit. Ensuite, l'opérande de droite est évalué. le résultat a son type et sa valeur.

La seule particularité est que le résultat de l'opérateur n'est pas utilisé dans l'instruction for. L'expression est utilisée pour ses effets secondaires.

Habituellement, le premier paramètre transmis à un programme en cours d'exécution est son nom. L'expression dans la clause-1 ignore ce premier paramètre.

Comparez la sortie de ces deux programmes. Supposons que l'utilisateur spécifie des paramètres de ligne de commande

first second third

Le programme de sortie de ce programme

#include <stdio.h>

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

    return 0;
}

est

first 
second 
third

et la sortie du programme de ce programme lorsque le clause-1 est vide (ni expression ni déclaration)

#include <stdio.h>

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

    return 0;
}

est

./prog.exe
first 
second 
third

Pour que l'opérateur de virgule soit clair, considérez un programme comme le premier programme de démonstration où, au lieu de la boucle for, une boucle while est utilisée.

#include <stdio.h>

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

    return 0;
}

Le résultat sera le même que dans le premier programme de démonstration

first 
second 
third

Voici dans l'instruction while, on utilise également l'opérateur virgule. La différence est que dans ce cas, la valeur de l'opérateur de virgule est utilisée comme valeur de la condition.

Faites attention à ce que le expression-3 représente également à lui seul une expression avec l'opérateur virgule.

De plus, comme la question est marquée avec la balise C++, vous devez savoir que, en C++, la deuxième clause de l'instruction for (en C++, elle s'appelle condition) peut également être une expression ou une déclaration.

27
Vlad from Moscow

Comme déjà indiqué dans plusieurs réponses, il s’agit de l’opérateur virgule , donc

argc--, argv++

est seulement un expression.

L'opérateur par virgule évalue les deux côtés, d'abord à gauche, puis à droite. Le résultat est celui du côté droit. Donc, vous pourriez écrire des choses étranges comme

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

cela ajouterait 5 à x avant d'attribuer le résultat de x + 2 à a. Un tel code est source de confusion et doit être évité. Mais cela démontre une propriété importante de l'opérateur virgule :

Il agit comme un point de séquence : Avec le code ci-dessus, vous avez la garantie que 5 a déjà été ajouté à x (la valeur de x en effet changé), avant l'évaluation de x + 2.

La principale utilisation judicieuse de l'opérateur de virgule est celle indiquée dans votre question. Il est utile dans les boucles for plus complexes d’avoir par exemple effets secondaires multiples et un séquençage garanti.

Pour clarifier la raison pour laquelle la séquence pourrait être importante (ce n'est pas dans votre exemple car les effets secondaires ne dépendent pas les uns des autres), regardez cet exemple (artificiel):

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

Si l'opérateur de virgule n'a pas introduit de point de séquence ici, vous ne saurez pas si j+=i ajouterait la valeur incrémentée i ou celle non incrémentée.

8
user2371524

Pour les initialisations multiples et les mises à jour/incrémentales multiples, nous utilisons comma operator(,). . Nous séparons chaque instance par un comma(,).
Dans ce cas, lors de la saisie de la boucle for, les deux expressions argc-- et argv++ de la partie initialisation sont exécutées. À partir de ce moment, à chaque fois que la boucle est itérée, les expressions argc-- et argv++ sont en la partie incrémentielle est exécutée. 

4
Praveen Kumar

Dans cette for boucle opérateur virgule est utilisé dans les première et dernière expressions. Donc, la déclaration for est comme 

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

Il n'y a que trois expressions (argc--, argv++), argc > 0 et (argc--, argv++).
L’expression 1 ne doit pas nécessairement être une déclaration, elle peut être toute expression valide ou même être omise. 

for(;expression2; expression3)

ou toutes les expressions peuvent être omises 

for(;;)  

Dans la boucle for, (argc--, argv++) est utilisé comme première expression pour mettre à jour les variables argc et argv (argc sera décrémenté de 1 et le pointeur argv sera incrémenté de 1). Une fois que l'effet secondaire sur ces variables est effectué, le programme entre dans le corps de la boucle après avoir vérifié argc > 0 pour true. C'est ce qui arrive quand tu fais 

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

i = 1 update i to 1 et la condition est vérifiée. Cette mise à jour de i n'est effectuée qu'une fois, puis pour le reste, elle est mise à jour par l'expression i++

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

Fait ce qui suit:

  1. Exécuter " Initialisation " partie: Decrement argc et increment argv
  2. Vérifiez si argv > 0, si ce n'est pas le cas, quittez la boucle.
  3. Exécuter { ... }
  4. Exécuter " Updation " partie: Decrement argc et increment argv
  5. Aller à l'étape 2. ci-dessus

Comme " Initialisation " et " Updation " sont identiques, cela pourrait aussi être écrit

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

Cette expression

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

se compose de trois sous-expressions séparées par l'opérateur virgule .

Ces sous-expressions sont exécutées de gauche à droite. 

L'expression entière évalue le résultat de la sous-expression la plus à droite.

1
alk

for ( argc--, argv++; argc > 0; argc--, argv++ ) signifie que la boucle commence avec les valeurs de argc, argv définies sur moins 1 et plus 1 de leurs valeurs initiales correspondantes. Chaque itération diminuera et augmentera leurs valeurs et s'arrêtera une fois que argc atteindra 0 (ce qui signifie que tous les arguments d'entrée ont été lus).

0
proton

Le paramètre d'initialisation dans la boucle for ne signifie pas uniquement l'initialisation d'une variable ayant une valeur particulière.

Il peut également avoir une ou plusieurs expressions régulières séparées par une virgule.

J'espère que ça va aider !!

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

peut être lu comme 

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

l'opérateur de virgule ayant la priorité la plus basse possible, l'opérateur de gauche sera toujours évalué en premier

0
Thomas.L

argv possède des arguments en ligne de commande. Cependant, le tout premier est le nom du programme.

Ainsi, la boucle commence à argv[1] et traite tous les arguments donnés par la ligne de commande sans traiter le nom du programme 

0
Antoine Morrier