Opérateurs

Écrit le 11/11/2004 par Wikibooks
Dernière mise à jour : 02/02/2006

Les opérateurs du C

Les opérateurs sont présentés par ordre de précédence.

opérateur arité associativité description
( ) gauche vers la droite (GD) parenthésage
() [] . -> GD appel de fonction, index de tableau, champ de structure ou de pointeur vers structure
! unaire droite vers la gauche (DG) négation booléenne
~ unaire DG négation binaire
++ -- unaire DG incrémentation et décrémentation
- unaire DG opposé
(type) unaire DG opérateur de transtypage (overcast, ou cast)
* unaire DG opérateur de déréférençage
& unaire DG opérateur de référençage
sizeof unaire DG fournit la taille en "char" de l'expression (souvent en octet mais pas toujours, mais sizeof(char) = 1 par définition)
* / % binaire GD multiplication, division, modulo (reste de la division)
+ - binaire GD addition, soustraction
>> << binaire GD décalages de bits
> >= < <= binaire GD comparaisons
== != binaire GD égalité/différence
& binaire GD et binaire
^ binaire GD ou exclusif binaire
| binaire GD ou inclusif binaire
&& binaire GD et logique
|| binaire GD ou logique
? : ternaire DG si...alors...sinon rapide
= += -= *= /= %= ^= &= |= >>= <<= binaire DG affectation
, binaire GD concaténation d'expressions

Propriétés des opérateurs C

Il est important de bien connaître les propriétés des opérateurs afin d'écrire des intructions en connaissance de cause. Certaines propriétés engendrent des comportements en apparence étranges, alors que le tout suit une logique très précise.

Post/pré incrémentation/décrémentation

C'est un concept quelque peu sibyllin du C. Incrémenter ou décrémenter de un est une opération extrêmement courante. Écrire à chaque fois 'variable = variable + 1' peut-être très pénible à longue. Le langage C a donc introduit des opérateurs raccourcis pour décrémenter ou incrémenter n'importe quel type atomique (gérable directement par le processeur : c'est à dire pas un tableau, ni une structure ou une union). Il s'agit des opérateurs '++' et '--', qui peuvent être utilisés de manière préfixée ou postfixée (avant ou après la variable).

Utilisé de manière préfixée, un opérateur modifiera la variable avant son usage, tandis qu'écrit de manière postfixée, la valeur sera utilisée d'abord, puis modifiée ensuite. Exemples :

int i = 0, j;

j = i++;        /* j vaut 0 et i vaut 1 */
j = --i;        /* j vaut 0 et i vaut 0 */

À noter que dans une expression, il est plus que déconseillé d'utiliser ce type d'opérateurs, si l'objet sur lequel il s'applique apparait plusieurs fois dans l'expression. Le code qui suit est imprévisible et ce simplement en changeant les options d'un même compilateur&nbsp;:

/* Le code qui suit est imprévisible */
j = i++ + ++i;

/* Ou de façon moins évidente */
j = i++ + fonction_qui_modifie( &i );

/* De même que ceci n'est pas conseillé */
i = i++;

Promotion

Il s'agit d'un aspect assez rebutant, mais qui permet une très bonne cohérence et une simplification des expressions. La promotion, comme son nom l'indique, est un mécanisme qui permet de convertir implicitement les nombres dans le format le plus grand utilisé dans l'expression.

L'exemple classique est lorsqu'on mélange des nombres réels avec des nombres entiers. Par exemple l'expression '2 / 3.' est de type double et vaudra effectivement deux tiers (0,666666...). L'expression '2 * a / 3' calculera les deux tiers de la variable 'a', et arrondira automatiquement à l'entier par défaut, les calculs ne faisant intervenir que des intructions sur les entiers (en supposant que 'a' soit un entier). À noter que l'expression '2 / 3 * a' vaudra... toujours zéro !

Plus subtile, l'expression '2 * a / 3.', en supposant toujours que a soit un entier, effectuera une multiplication entière entre 2 et a, puis promouvra le résultat en réel (double) puis effectuera la division réelle avec trois, pour obtenir un résultat réel lui-aussi.

Enfin un cas où les promotions peuvent surprendre, c'est lorsqu'on mélange des entiers signés/non-signés, plus particulièrement dans les comparaisons. Considérez le code suivant :

/* Ce code contient un effet de bord sibyllin */
unsigned long a =  23;
char          b = -23;

printf( "a %c b\n", a < b ? '<' : (a == b ? '=' : '>') );

De toute évidence a est supérieur à b dans cet exemple. Et pourtant, si ce code est exécuté, il affichera a < b, justement à cause de cette promotion.

Tout d'abord dans la comparaison a < b, le type de a est le plus grand, donc b est promu en unsigned long. C'est en fait ça le problème. -23 devient 4294967273 sur une architecture 32bits (qui est en fait la représentation en complément à 2 de -23), dans ce cas là effectivement 23 < 4294967273, d'où ce résultat surprenant.

À noter qu'on aurait eu le même problème si le type de b était signed long. En fait les types signés sont promus en non-signés, s'il y a au moins une variable non-signées dans l'expression.

Dans ce cas présent, il faut effectuer soit même le transtypage :

printf( "a %c b\n", (long) a < b ? '<' : ((long)a == b ? '=' : '>') );

Évaluation des expressions booléennes

Le C ne possède pas de type booléen dédié. Dans ce langage n'importe quelle valeur différente de zéro est considérée vraie, zéro étant considéré comme faux. Ce qui veut dire que n'importe quelle expression peut être utilisée à l'intérieur des tests (entier, réels, pointeurs, tableaux, etc.). Cela peut conduire à des expressions pas toujours très claire, comme :

int a = 0;
int b = 2;

if( a = b )
{
    /* Le code qui suit sera toujours exécuté ... */
}

Dans cet exemple, on ne teste pas si a est égal à b, mais on affecte la variable b à la variable a. Le résultat de l'expression étant 2, elle est donc toujours considérée vraie... Ce genre de raccourci est en général à éviter, et de nombreux compilateurs obligent à parenthéser ce genre d'expression sous peine d'émettre un avertissement. L'opérateur d'égalité en C s'écrit '=='.

Les opérateurs logiques de comparaisons (&& et ||, similaires sémantiquement à leur équivalent binaire & et |) évaluent leurs opérandes en circuit court. Dans le cas du ET logique (&&), si l'opérande gauche s'évalue à faux, on sait déjà que le résultat du ET sera faux et donc ce n'est pas la peine d'évaluer l'opérande droite. De la même manière si l'opérande gauche d'un OU logique (||) est évalué à vrai, le résultat sera aussi vrai et donc l'évaluation de l'opérande droite est inutile. Ceci permet d'écrire des expressions de ce genre, sans générer d'exception de l'unité arithmétique du processeur :

if( z != 0 && a / z < 10 )
{
    printf("Tout va bien\n");
}

Cet article provient de Wikibooks et est sous licence GNU Free Documentation License. Il a été écrit par plusieurs personnes et est constamment mis à jour. Cet article est la version du 1er mars 2005 à 11:21. L'article d'origine se trouve à http://fr.wikibooks.org/wiki/Programmation_C_Opérateurs.