Que fait l’opérateur de virgule?

Que fait l’opérateur, en C?

L’expression:

 (expression1, expression2) 

La première expression1 est évaluée, puis expression2 est évaluée et la valeur de expression2 est renvoyée pour l’expression entière.

J’ai vu plus utilisé dans les boucles while :

 ssortingng s; while(read_ssortingng(s), s.len() > 5) { //do something } 

Il va faire l’opération, puis faire un test basé sur un effet secondaire. L’autre façon serait de le faire comme ceci:

 ssortingng s; read_ssortingng(s); while(s.len() > 5) { //do something read_ssortingng(s); } 

L’opérateur virgule combine les deux expressions de part et d’autre de celui-ci en les évaluant dans l’ordre de gauche à droite. La valeur du côté droit est renvoyée comme valeur de l’expression entière. (expr1, expr2) est comme { expr1; expr2; } { expr1; expr2; } { expr1; expr2; } mais vous pouvez utiliser le résultat de expr2 dans un appel de fonction ou une affectation.

Il est souvent vu dans les boucles pour initialiser ou maintenir plusieurs variables comme ceci:

 for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh) { /* do something with low and high and put new values in newlow and newhigh */ } 

En dehors de cela, je ne l'ai utilisé que "dans la colère" dans un autre cas, lorsque vous avez terminé deux opérations qui devaient toujours aller ensemble dans une macro. Nous avions du code qui copiait diverses valeurs binarys dans un tampon d'octets pour l'envoi sur un réseau, et un pointeur maintenu là où nous étions parvenus à:

 unsigned char outbuff[BUFFSIZE]; unsigned char *ptr = outbuff; *ptr++ = first_byte_value; *ptr++ = second_byte_value; send_buff(outbuff, (int)(ptr - outbuff)); 

Là où les valeurs étaient short s ou int s nous avons fait ceci:

 *((short *)ptr)++ = short_value; *((int *)ptr)++ = int_value; 

Plus tard, nous avons lu que c n'était pas vraiment valide, car (short *)ptr n'est plus une valeur l et ne peut pas être incrémenté, bien que notre compilateur à l'époque ne s'en souciait pas. Pour résoudre ce problème, nous avons divisé l'expression en deux:

 *(short *)ptr = short_value; ptr += sizeof(short); 

Cependant, cette approche reposait sur le fait que tous les développeurs se rappelaient de mettre les deux déclarations en permanence. Nous voulions une fonction où vous pouviez passer le pointeur de sortie, la valeur et le type de valeur. Ceci étant C, pas C ++ avec des templates, nous ne pouvions pas avoir une fonction prenant un type arbitraire, donc nous avons opté pour une macro:

 #define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type)) 

En utilisant l'opérateur de virgule, nous avons pu l'utiliser dans des expressions ou des déclarations comme nous le souhaitions:

 if (need_to_output_short) ASSIGN_INCR(ptr, short_value, short); latest_pos = ASSIGN_INCR(ptr, int_value, int); send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff)); 

Je ne suggère aucun de ces exemples sont bon style! En effet, il semble que je me souvienne du code complet de Steve McConnell déconseillant d’utiliser des opérateurs de virgule dans une boucle for : pour la lisibilité et la maintenabilité, la boucle ne devrait être contrôlée que par une seule variable et les expressions code, pas d'autres bits supplémentaires d'initialisation ou de maintenance de la boucle.

L’ opérateur de virgule évalue l’opérande gauche, supprime le résultat, puis évalue le bon opérande et ce sera le résultat. L’utilisation idiomatique indiquée dans le lien est l’initialisation des variables utilisées dans une boucle for , et donne l’exemple suivant:

 void rev(char *s, size_t len) { char *first; for ( first = s, s += len - 1; s >= first; --s) /*^^^^^^^^^^^^^^^^^^^^^^^*/ putchar(*s); } 

Autrement, il n’ya pas beaucoup d’utilisateurs de l’ opérateur virgule , bien qu’il soit facile d’abuser pour générer du code difficile à lire et à gérer.

À partir du projet de norme C99, la grammaire est la suivante:

 expression: assignment-expression expression , assignment-expression 

et le paragraphe 2 dit:

L’ opérande gauche d’un opérateur de virgule est évaluée en tant qu’expression vide; il y a un sharepoint séquence après son évaluation. Ensuite, le bon opérande est évalué; le résultat a son type et sa valeur. 97) Si vous tentez de modifier le résultat d’un opérateur de virgule ou d’y accéder après le prochain sharepoint séquence, le comportement n’est pas défini.

Note de bas de page 97 dit:

Un opérateur de virgule ne produit pas de lvalue .

ce qui signifie que vous ne pouvez pas affecter le résultat de l’ opérateur de virgule .

Il est important de noter que l’opérateur de virgule a la priorité la plus faible et que, par conséquent, l’utilisation de () peut faire une grande différence, par exemple:

 #include  int main() { int x, y ; x = 1, 2 ; y = (3,4) ; printf( "%d %d\n", x, y ) ; } 

aura la sortie suivante:

 1 4 

Cela provoque l’évaluation de plusieurs instructions, mais n’utilise que la dernière comme valeur résultante (rvalue, je pense).

Alors…

 int f() { return 7; } int g() { return 8; } int x = (printf("assigning x"), f(), g() ); 

devrait aboutir à x défini sur 8.

L’opérateur de virgule n’a rien de significatif, c’est une fonctionnalité 100% superflue. Son utilisation principale est “les personnes essayant d’être intelligentes” et l’utilisent donc pour dissimuler (involontairement) du code lisible. Le principal domaine d’utilisation consiste à masquer les boucles, par exemple:

 for(int i=0, count=0; i 

int i=0, count=0 n'est en fait pas l'opérateur virgule, mais une liste de déclaration (nous sums déjà confus ici). i++, count++ est l'opérateur virgule, qui évalue d'abord l'opérande gauche, puis l'opérande droit. Le résultat de l'opérateur de virgule est le résultat de l'opérande droit. Le résultat de l'opérande gauche est supprimé.

Mais le code ci-dessus pourrait être écrit de manière beaucoup plus lisible sans l'opérateur virgule:

 int count = 0; for(int i=0; i 

La seule utilisation réelle de l'opérateur de virgule que j'ai vue est la discussion artificielle sur les points de séquence, car l'opérateur de virgule est accompagné d'un sharepoint séquence entre l'évaluation des opérandes gauche et droit.

Donc, si vous avez un code de comportement indéfini comme celui-ci:

 printf("%d %d", i++, i++); 

Vous pouvez réellement le transformer en comportement non spécifié (ordre d’évaluation des parameters de la fonction) en écrivant

 printf("%d %d", (0,i++), (0,i++)); 

Il y a maintenant un sharepoint séquence entre chaque évaluation de i++ , de sorte qu'au moins le programme ne risque pas de tomber en panne et de graver plus longtemps, même si l'ordre d'évaluation des parameters de fonction rest indéterminé.

Bien sûr, personne n’écrirait un tel code dans des applications réelles, cela ne serait utile que pour les discussions entre experts en langue sur les points de séquence en langage C.

L'opérateur de virgule est interdit par MISRA-C: 2004 et MISRA-C: 2012 avec la justification qu'il crée un code moins lisible.

Comme les réponses précédentes l’ont déclaré, il évalue toutes les instructions mais utilise la dernière comme valeur de l’expression. Personnellement, je ne l’ai trouvé utile que dans les expressions en boucle:

 for (tmp=0, i = MAX; i > 0; i--) 

Le seul endroit que j’ai vu utile est lorsque vous écrivez une boucle funky où vous voulez faire plusieurs choses dans l’une des expressions (probablement l’expression init ou l’expression de boucle. Quelque chose comme:

 bool arraysAreMirrored(int a1[], int a2[], size_t size) { size_t i1, i2; for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--) { if(a1[i1] != a2[i2]) { return false; } } return true; } 

Pardonnez-moi s'il y a des erreurs de syntaxe ou si j'ai mélangé quelque chose qui n'est pas ssortingct C. Je ne prétends pas que l'opérateur, est une bonne forme, mais c'est ce que vous pourriez l'utiliser. Dans le cas ci-dessus, j'utiliserais probablement une boucle while afin que les expressions multiples sur init et loop soient plus évidentes. (Et j'initialiserai i1 et i2 en ligne au lieu de déclarer puis d'initialiser .... bla bla bla.)