Opérateur virgule - Comma operator
Dans les langages de programmation C et C++ , l' opérateur virgule (représenté par le jeton , ) est un opérateur binaire qui évalue son premier opérande et rejette le résultat, puis évalue le deuxième opérande et renvoie cette valeur (et ce type) ; il y a un point de séquence entre ces évaluations.
L'utilisation du jeton virgule en tant qu'opérateur est distincte de son utilisation dans les appels de fonction et les définitions, les déclarations de variables, les déclarations d'énumération et les constructions similaires, où il agit comme un séparateur .
Syntaxe
L'opérateur virgule sépare les expressions (qui ont une valeur) d'une manière analogue à la façon dont le point - virgule termine les instructions, et les séquences d'expressions sont mises entre parenthèses de la même manière que les séquences d'instructions sont mises entre accolades : (a, b, c)est une séquence d'expressions, séparées par des virgules, qui évalue à la dernière expression cwhile {a; b; c;}est une séquence d'instructions et n'évalue à aucune valeur. Une virgule ne peut apparaître qu'entre deux expressions - des expressions séparées par des virgules - contrairement au point-virgule, qui se produit à la fin d'une instruction (non-bloc) - les points-virgules terminent les instructions.
L'opérateur virgule a la priorité la plus basse de tous les opérateurs C et agit comme un point de séquence . Dans une combinaison de virgules et de points-virgules, les points-virgules ont une priorité inférieure aux virgules, car les points-virgules séparent les instructions, mais les virgules apparaissent à l'intérieur des instructions, ce qui correspond à leur utilisation en tant que ponctuation ordinaire : a, b; c, dest groupé comme (a, b); (c, d)parce qu'il s'agit de deux instructions distinctes.
Exemples
Dans cet exemple, le comportement différent entre les deuxième et troisième lignes est dû au fait que l'opérateur virgule a une priorité inférieure à l'affectation. Le dernier exemple diffère également puisque l'expression de retour doit être entièrement évaluée avant que la fonction puisse retourner.
/**
* Commas act as separators in this line, not as an operator.
* Results: a=1, b=2, c=3, i=0
*/
int a=1, b=2, c=3, i=0;
/**
* Assigns value of b into i.
* Commas act as separators in the first line and as an operator in the second line.
* Results: a=1, b=2, c=3, i=2
*/
int a=1, b=2, c=3;
int i = (a, b);
/**
* Assigns value of a into i.
* Equivalent to: int i = a; int b;
* Commas act as separators in both lines.
* The braces on the second line avoid variable redeclaration in the same block,
* which would cause a compilation error.
* The second b declared is given no initial value.
* Results: a=1, b=2, c=3, i=1
*/
int a=1, b=2, c=3;
{ int i = a, b; }
/**
* Increases value of a by 2, then assigns value of resulting operation a + b into i.
* Commas act as separators in the first line and as an operator in the second line.
* Results: a=3, b=2, c=3, i=5
*/
int a=1, b=2, c=3;
int i = (a += 2, a + b);
/**
* Increases value of a by 2, then stores value of a to i, and discards unused
* values of resulting operation a + b.
* Equivalent to: (i = (a += 2)), a + b;
* Commas act as separators in the first line and as an operator in the third line.
* Results: a=3, b=2, c=3, i=3
*/
int a=1, b=2, c=3;
int i;
i = a += 2, a + b;
/**
* Assigns value of a into i.
* Commas act as separators in both lines.
* The braces on the second line avoid variable redeclaration in the same block,
* which would cause a compilation error.
* The second b and c declared are given no initial value.
* Results: a=1, b=2, c=3, i=1
*/
int a=1, b=2, c=3;
{ int i = a, b, c; }
/**
* Commas act as separators in the first line and as an operator in the second line.
* Assigns value of c into i, discarding the unused a and b values.
* Results: a=1, b=2, c=3, i=3
*/
int a=1, b=2, c=3;
int i = (a, b, c);
/**
* Returns 6, not 4, since comma operator sequence points following the keyword
* return are considered a single expression evaluating to rvalue of final
* subexpression c=6.
* Commas act as operators in this line.
*/
return a=4, b=5, c=6;
/**
* Returns 3, not 1, for same reason as previous example.
* Commas act as operators in this line.
*/
return 1, 2, 3;
/**
* Returns 3, not 1, still for same reason as above. This example works as it does
* because return is a keyword, not a function call. Even though compilers will
* allow for the construct return(value), the parentheses are only relative to "value"
* and have no special effect on the return keyword.
* Return simply gets an expression and here the expression is "(1), 2, 3".
* Commas act as operators in this line.
*/
return(1), 2, 3;
Les usages
L'opérateur virgule a des cas d'utilisation relativement limités. Parce qu'il rejette son premier opérande, il n'est généralement utile que lorsque le premier opérande a des effets secondaires souhaitables qui doivent être séquencés avant le deuxième opérande. De plus, parce qu'il est rarement utilisé en dehors d'expressions idiomatiques spécifiques et qu'il est facilement confondu avec d'autres virgules ou le point-virgule, il est potentiellement déroutant et sujet aux erreurs. Néanmoins, il existe certaines circonstances où il est couramment utilisé, notamment dans les boucles for et dans SFINAE . Pour les systèmes embarqués qui peuvent avoir des capacités de débogage limitées, l'opérateur virgule peut être utilisé en combinaison avec une macro pour remplacer de manière transparente un appel de fonction, pour insérer du code juste avant l'appel de fonction.
Pour les boucles
L'utilisation la plus courante consiste à autoriser plusieurs instructions d' affectation sans utiliser d'instruction de bloc, principalement dans l'initialisation et les expressions d'incrémentation d'une boucle for . C'est la seule utilisation idiomatique en programmation C élémentaire. Dans l'exemple suivant, l'ordre des initialiseurs de la boucle est significatif :
void rev(char *s, size_t len)
{
char *first;
for (first = s, s += len; s >= first; --s) {
putchar(*s);
}
}
Une solution alternative à ce problème dans d'autres langages est l' affectation parallèle , qui permet à plusieurs affectations de se produire dans une seule instruction, et utilise également une virgule, bien qu'avec une syntaxe et une sémantique différentes. Ceci est utilisé dans Go dans sa boucle for analogue.
En dehors des initialiseurs de boucle for (qui ont une utilisation spéciale des points-virgules), la virgule peut être utilisée à la place d'un point-virgule, en particulier lorsque les instructions en question fonctionnent de la même manière qu'un incrément de boucle (par exemple à la fin d'une boucle while) :
++p, ++q;
++p; ++q;
Macro
La virgule peut être utilisée dans les macros de préprocesseur pour effectuer plusieurs opérations dans l'espace d'une seule expression syntaxique.
Une utilisation courante consiste à fournir des messages d'erreur personnalisés dans les assertions ayant échoué. Cela se fait en passant une liste d'expressions entre parenthèses à la assertmacro, où la première expression est une chaîne d'erreur et la deuxième expression est la condition affirmée. La assertmacro renvoie son argument textuellement en cas d'échec d'assertion. Ce qui suit est un exemple:
#include <stdio.h>
#include <assert.h>
int main ( void )
{
int i;
for (i=0; i<=9; i++)
{
assert( ( "i is too big!", i <= 4 ) );
printf("i = %i\n", i);
}
return 0;
}
Production:
i = 0 i = 1 i = 2 i = 3 i = 4 assert: assert.c:6: test_assert: Assertion `( "i is too big!", i <= 4 )' failed. Aborted
Cependant, la macro assert est généralement désactivée dans le code de production, utilisez-la donc uniquement à des fins de débogage.
État
La virgule peut être utilisée dans une condition (d'un if, while, do while, ou for) pour permettre des calculs auxiliaires, notamment l'appel d'une fonction et l'utilisation du résultat, avec une portée de bloc :
if (y = f(x), y > x) {
... // statements involving x and y
}
Un idiome similaire existe dans Go , où la syntaxe de l'instruction if autorise explicitement une instruction facultative.
Retour complexe
La virgule peut être utilisée dans les instructions de retour, pour affecter une variable globale ou un paramètre de sortie (passé par référence). Cet idiome suggère que les affectations font partie du retour, plutôt que des affectations auxiliaires dans un bloc qui se termine avec le retour réel. Par exemple, en définissant un numéro d'erreur global :
if (failure)
return (errno = EINVAL, -1);
Cela peut s'écrire de façon plus verbeuse comme :
if (failure) {
errno = EINVAL;
return -1;
}
Eviter un blocage
Par souci de concision, la virgule peut être utilisée pour éviter un bloc et les accolades associées, comme dans :
if (x == 1) y = 2, z = 3;
if (x == 1)
y = 2, z = 3;
à la place de:
if (x == 1) {y = 2; z = 3;}
if (x == 1) {
y = 2; z = 3;
}
Autres langues
Dans les langages de programmation OCaml et Ruby , le point-virgule (";") est utilisé à cette fin. JavaScript et Perl utilisent l'opérateur virgule de la même manière que C/C++. En Java , la virgule est un séparateur utilisé pour séparer les éléments d'une liste dans divers contextes. Ce n'est pas un opérateur et n'évalue pas le dernier élément de la liste.
Voir également
Les références
Bibliographie
- Ramajaran, V. (1994), Computer Programming in C , New Delhi : Prentice Hall of India
- Dixit, JB (2005), Fondamentaux de l'informatique et de la programmation en C , New Delhi : Laxmi Publications
- Kernighan, Brian W.; Ritchie, Dennis M. (1988), Le langage de programmation C (2e éd.), Englewood Cliffs, NJ : Prentice Hall
Liens externes
- " Effet de l'utilisation d'une virgule au lieu d'un point-virgule en C et C++ ", Stack Overflow
- Boost.Assignment – fonction de la bibliothèque Boost qui facilite le remplissage des conteneurs en surchargeant l'opérateur virgule