Que fait l' ,
opérateur en C?
Que fait l' ,
opérateur en C?
Réponses:
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.
i
les valeurs 5, 4, 3, 2 ou 1. C'est simplement 0. C'est pratiquement inutile à moins que les expressions aient des effets secondaires.
i = b, c;
équivaut à (i = b), c
parce que l'attribution =
a une priorité plus élevée que l'opérateur virgule ,
. L'opérateur virgule a la priorité la plus basse de tous.
expression1, expression2;
abord expression1
est évalué, probablement pour ses effets secondaires (comme l'appel d'une fonction), puis il y a un point de séquence, puis expression2
est évalué et la valeur est renvoyée…
J'ai vu le plus utilisé dans les while
boucles:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
Il fera l'opération, puis fera un test basé sur un effet secondaire. L'autre façon serait de le faire comme ceci:
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
while (read_string(s) && s.len() > 5)
. De toute évidence, cela ne fonctionnerait pas si read_string
n'a pas de valeur de retour (ou n'a pas de valeur significative). (Edit: Désolé,
while (1)
avec une break;
déclaration dans le corps. Essayer de forcer la partie break-out du code dans le test while ou dans le test do-while, est souvent un gaspillage d'énergie et rend le code plus difficile à comprendre.
while(1)
et break
;
L' opérateur virgule évaluera l'opérande gauche, rejettera le résultat, puis évaluera l'opérande droit et ce sera le résultat. L' utilisation idiomatique comme indiqué dans le lien est lors de l'initialisation des variables utilisées dans une for
boucle, et il donne l'exemple suivant:
void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
}
Sinon, il n'y a pas beaucoup de bonnes utilisations de l' opérateur virgule , bien qu'il soit facile d'abuser de générer du code difficile à lire et à maintenir.
À 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 virgule est évalué comme une expression vide; il y a un point de séquence après son évaluation. Ensuite, l' opérande droit est évalué; le résultat a son type et sa valeur. 97) Si une tentative est faite pour modifier le résultat d'un opérateur virgule ou pour y accéder après le point de séquence suivant, le comportement n'est pas défini.
La note de bas de page 97 dit:
Un opérateur virgule ne produit pas de lvalue .
ce qui signifie que vous ne pouvez pas affecter le résultat de l' opérateur virgule .
Il est important de noter que l'opérateur virgule a la priorité la plus basse et qu'il existe donc des cas où l'utilisation ()
peut faire une grande différence, par exemple:
#include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf( "%d %d\n", x, y ) ;
}
aura la sortie suivante:
1 4
L'opérateur virgule combine les deux expressions de chaque côté en une seule, en les évaluant toutes les deux 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 similaire { expr1; expr2; }
mais vous pouvez utiliser le résultat de expr2
dans un appel ou une affectation de fonction.
On le voit souvent dans des for
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é «en colère» que dans un autre cas, lors de la conclusion de deux opérations qui devraient toujours aller ensemble dans une macro. Nous avions du code qui copiait diverses valeurs binaires dans un tampon d'octets pour l'envoi sur un réseau, et un pointeur maintenu là où nous en étions:
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 ce n'était pas vraiment un C valide, car ce (short *)ptr
n'est plus une valeur l et ne peut pas être incrémenté, bien que notre compilateur à l'époque ne s'en soit pas soucié. 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 souvenaient de mettre les deux déclarations tout le temps. Nous voulions une fonction où vous pourriez passer le pointeur de sortie, la valeur et et le type de la valeur. Ceci étant C, pas C ++ avec des modèles, nous ne pouvions pas avoir une fonction prendre un type arbitraire, nous nous sommes donc installés sur une macro:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
En utilisant l'opérateur virgule, nous avons pu l'utiliser dans des expressions ou comme instructions 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 dis pas qu'aucun de ces exemples n'est de bon style! En effet, il me semble me souvenir de Code Complete de Steve McConnell déconseillant même d'utiliser des opérateurs de virgule dans une for
boucle: pour la lisibilité et la maintenabilité, la boucle doit être contrôlée par une seule variable, et les expressions dans la for
ligne elle-même ne doivent contenir que du code de contrôle de boucle, pas d'autres bits supplémentaires d'initialisation ou de maintenance de boucle.
Cela provoque l'évaluation de plusieurs déclarations, 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 à la mise à 8 de x.
Le seul endroit où je l'ai vu être 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 la 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 strict C. Je ne dis pas que l'opérateur, est de bonne forme, mais c'est pour cela que vous pouvez l'utiliser. Dans le cas ci-dessus, j'utiliserais probablement une while
boucle à la place, de sorte que les multiples expressions sur init et loop seraient plus évidentes. (Et j'initialiserais i1 et i2 en ligne au lieu de déclarer puis d'initialiser .... bla bla bla.)
Je le ressuscite simplement pour répondre aux questions de @Rajesh et @JeffMercado qui, à mon avis, sont très importantes car c'est l'un des meilleurs résultats des moteurs de recherche.
Prenons par exemple l'extrait de code suivant
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
Il imprimera
1 5
Le i
cas est traité comme expliqué par la plupart des réponses. Toutes les expressions sont évaluées dans l'ordre de gauche à droite, mais seule la dernière est affectée i
. Le résultat de l' (
expression ) is
1`.
Le j
cas suit des règles de priorité différentes car il ,
a la priorité d'opérateur la plus basse. En raison de ces règles, le compilateur voit l' expression d'affectation, constante, constante ... . Les expressions sont évaluées à nouveau dans l' ordre et leurs effets secondaires à droite gauche restent visibles, donc j
est 5
en raison de j = 5
.
Fait intéressant, int j = 5,4,3,2,1;
n'est pas autorisé par la spécification de la langue. Un initialiseur attend une expression d'affectation, donc un ,
opérateur direct n'est pas autorisé.
J'espère que cela t'aides.