Question

Ce qui ne l' , l'opérateur n'a en C?

Était-ce utile?

La solution

L'expression:

(expression1,  expression2)

Première expression1 est évaluée, puis expression2 est évaluée et la valeur de expression2 est retourné pour l'ensemble de l'expression.

Autres conseils

J'ai vu plus utilisé dans while boucles:

string s;
while(read_string(s), s.len() > 5)
{
   //do something
}

Il fera l'opération, puis faire un essai basé sur un effet secondaire.L'autre solution serait de faire comme ceci:

string s;
read_string(s);
while(s.len() > 5)
{
   //do something
   read_string(s);
}

L' opérateur virgule permettra d'évaluer l'opérande de gauche, jeter le résultat et d'en évaluer l'opérande de droite et qui sera le résultat.L' idiomatiques utilisation comme indiqué dans le lien est lors de l'initialisation des variables utilisées dans un 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 grand les utilisations de l' opérateur virgule, mais il est facile d'abus pour générer le code qui est difficile à lire et à maintenir.

À partir de la projet de norme C99 la grammaire est comme suit:

expression:
  assignment-expression
  expression , assignment-expression

et le paragraphe 2 de l' dit:

L' opérande de gauche d'un opérateur virgule est évalué comme un vide d'expression; il y a un point de séquence après son évaluation.Puis le opérande de droite est évaluée;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 d'y accéder après le prochain point de séquence, le comportement est indéfini.

Note de bas de page 97 dit:

Un opérateur virgule n' pas d'obtenir une 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 la plus basse priorité et donc il y a des cas où l'utilisation () peuvent 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 le résultat suivant:

1 4

L'opérateur virgule combine les deux expressions de chaque côté en un seul, de les évaluer à la fois de gauche à droite.La valeur de la main droite est retourné en tant que valeur de l'expression complète. (expr1, expr2) c'est comme { expr1; expr2; } mais vous pouvez utiliser le résultat de expr2 dans un appel de fonction ou de la cession.

Il est souvent vu dans les for boucles d'initialiser ou de maintenir de multiples 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" dans les autres cas, lorsque l'emballage jusqu'à deux opérations qui devraient toujours aller de pair dans une macro.Nous avons eu code copié différentes valeurs binaires dans un tampon d'octets pour l'envoi sur un réseau, et un pointeur maintenue lorsque nous avons eu jusqu'à:

unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;

*ptr++ = first_byte_value;
*ptr++ = second_byte_value;

send_buff(outbuff, (int)(ptr - outbuff));

Où les valeurs ont été shorts ou ints nous l'avons fait:

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

Plus tard, nous avons lu que ce n'était pas vraiment valide C, parce que (short *)ptr n'est plus une l-value et ne peut pas être incrémenté, bien que notre compilateur à la fois, n'a pas l'esprit.Pour résoudre ce problème, nous avons divisé l'expression en deux:

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

Cependant, cette approche se fondait sur tous les développeurs à se rappeler de mettre les deux états, dans tous les temps.Nous voulions une fonction où l'on pouvait passer le pointeur de sortie, la valeur et le type de la valeur.Ceci étant C, pas du C++ à l'aide de modèles, nous ne pourrions pas avoir une prise de fonction d'un type arbitraire, de sorte que nous nous sommes 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 été en mesure de l'utiliser dans des expressions ou des énoncés comme nous l'avions souhaité:

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 que l'un de ces exemples sont un bon style!En effet, je crois me souvenir de Steve McConnell Le Code Complet conseiller à l'encontre même de l'aide de la virgule opérateurs dans un for boucle:pour des raisons de lisibilité et la maintenabilité, la boucle doit être contrôlé par une seule variable, et les expressions dans le for la ligne elle-même ne doit contenir que la boucle de contrôle de code, pas d'autres morceaux de l'initialisation ou de la boucle de l'entretien.

Elle provoque de l'évaluation de plusieurs états, mais utilise seulement la dernière en tant que valeur qui en résulte (rvalue, je pense).

Alors...

int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

devrait, x étant fixé à 8.

L'opérateur virgule n'a rien d'intéressant, c'est un 100% fonctionnalité superflue.La principale utilisation de la il est "les gens d'essayer d'être intelligent" et donc l'utiliser pour (involontairement) à obscurcir un code lisible.Le principal domaine d'utilisation est source de confusion pour les boucles, par exemple:

for(int i=0, count=0; i<x; i++, count++)

int i=0, count=0 n'est pas réellement de l'opérateur virgule, mais une déclaration de la liste (nous sommes déjà confondu ici). i++, count++ est l'opérateur virgule, qui évalue l'opérande de gauche d'abord, puis l'opérande de droite.Le résultat de l'opérateur virgule est le résultat de l'opérande de droite.Le résultat de l'opérande de gauche est rejetée.

Mais le code ci-dessus peut être écrite d'une manière beaucoup plus lisible sans l'opérateur virgule:

int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
  ...
  count++;
}

La seule réelle d'utilisation de l'opérateur virgule que j'ai vu, est artificielle discussions à propos de la séquence de points, puisque l'opérateur virgule est livré avec un point de séquence entre l'évaluation de la gauche et la droite opérandes.

Donc, si vous avez quelques un comportement non défini code comme ceci:

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

Vous pouvez réellement transformer en simplement un comportement non spécifié (de l'ordre d'évaluation des paramètres de la fonction) par écrit

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

Il y a maintenant un point de séquence entre chaque évaluation de i++, donc, à moins que le programme ne risque pas de crash et brûler plus longtemps, même si l'ordre d'évaluation des paramètres de la fonction reste indéterminée.

Bien sûr, personne ne serait d'écrire un tel code dans des applications réelles, il est seulement utile pour la langue-avocat discussions à propos de la séquence de points dans le langage C.

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

Comme les réponses précédentes ont indiqué qu'il évalue toutes les déclarations, mais utilise la dernière parce que la valeur de l'expression.Personnellement, je n'ai trouvé utile dans la boucle expressions:

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

Le seul endroit où j'ai vu qu'il soit utile, c'est quand vous écrivez un funky boucle où vous voulez le faire de multiples choses dans l'une des expressions (probablement l'init de l'expression ou de boucle d'expression.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 si il y a des erreurs de syntaxe ou si j'ai mélangé tout ce qui n'est pas stricte C.Je ne veux pas dire que la , opérateur de la bonne forme, mais c'est ce que vous pouvez l'utiliser pour.Dans le cas ci-dessus, je serais probablement utiliser un while boucle au lieu de sorte que les multiples expressions sur init et la boucle serait plus évident.(Et je serais initialiser i1 et i2 inline au lieu de déclarer et puis en cours d'initialisation....bla bla bla.)

Je suis à la relance de ce tout simplement pour répondre aux questions de @Rajesh et @JeffMercado qui, je pense, sont très importants, car c'est l'un des meilleurs moteur de recherche, de hits.

Prendre l'extrait de code suivant par exemple

int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);

Il apparaîtra à l'impression

1 5

L' i cas est traité comme expliqué par la plupart des réponses.Toutes les expressions sont évaluées de gauche à droite, mais seul le dernier est attribué à i.Le résultat de l' ( expression )is1`.

L' j cas ne suit pas les mêmes règles de priorité depuis , a la plus faible priorité de l'opérateur.En raison de ces règles, le compilateur voit l'affectation d'expression, constant, constant ....Les expressions sont à nouveau évaluées de gauche à droite et de leurs effets secondaires restent visibles, par conséquent, j est 5 comme un résultat de j = 5.

Interstingly, int j = 5,4,3,2,1; n'est pas autorisé par la langue spec.Un initialiseur s'attend à une l'affectation d'expression donc direct , l'opérateur n'est pas autorisé.

Espérons que cette aide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top