Programmation C source/opérateurs
int main(void)
 printf("----------------------------------------\n");
 printf(" Les opérateurs du C\n");
 printf("----------------------------------------\n");
 /* Rappel :
 Les déclarations et définitions :
 (variables, fonctions, types)
 déclarent et définissent les objets que pourront manipuler le programme.
 exemple : int i
 Les expressions :
 manipulent les déclarations, via les opérateurs.
 exemple : i + 1
 Les instructions :
 manipulent les expressions pour leur donner une signification particulière.
 (instruction simple, test, boucle, saut, ... etc.)
 exemple : i = i + 1;
 exemple : if (i > 0) printf("i est positif !\n");
 Les opérateurs du C permettent de former des expressions.
 La priorité (quel opérateur est appliqué avant, en l'absence de parenthèses
 explicite) et l'associativité (dans quel ordre sont traités les arguments des
 opérateurs ayant la même priorité) sont résumées dans la table suivante (par ordre
 décroissant de priorité).
 GD gauche vers la droite
 DG droite vers la gauche
opérateur arité associativité description
( ) GD parenthésage
() [] . -> GD appel de fonction, index de tableau, membre de
 structure, pointe sur membre de structure
! unaire DG négation booléenne
~ unaire DG négation binaire
 -- unaire DG incrémentation et décrémentati.
- unaire DG opposé
& unaire DG opérateur de référençage
sizeof unaire DG fournit la taille en nombre de "char" de l'expression
 (souvent en octet mais pas toujours, mais
 sizeof(char) == 1 par définition, voir Caractères)
 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 et logique avec séquencement
? : ternaire DG si...alors...sinon
= += -= *= /= %= ^= &= |= »= «= binaire DG affectation
, binaire GD séquencement
 printf("\n----------------------------------------\n");
 printf(" Post/pré incrémentation/décrémentation\n");
 printf("----------------------------------------\n");
 /* L'incrémentation c'est écrire ++i ou i++ à la place de i = i + 1
 Utilisé de manière préfixée, l'opérateur retourne la valeur incrémentée, tandis
 qu'utilisé de manière postfixée l'opérateur retourne la valeur originale */
 int i = 0, resultat;
 resultat = ++i;
 // resultat vaut 1 et i vaut 1
 resultat = i++;
 // resultat vaut 1 et i vaut 2
 /* À noter que le langage ne garantit que la variable incrémentée n'aura sa nouvelle
 valeur qu'une fois le prochain point de séquencement atteint (généralement la fin
 d'une instruction). Ainsi, si l'objet sur lequel s'applique un tel opérateur
 apparaît plusieurs fois avant un point de séquencement, alors le résultat est
 imprévisible. */
 i = 2;
 i = i++;
 /* le code est imprévisible
 soit on fait d'abord i++ donc i = 3
 puis i = 2 (++ postfixée l'opérateur retourne la valeur originale)
 donc i vaut 2
 soit on fait d'abord i = i++ soit i = 2
 puis i++ soit i = 3
 donc i = 3 */
 i = 2;
 #if 0
 j = f(++i, 22, i);
 /* idem le code est imprévisible
 on fera j = f(3, 22, 2);
 ou j = f(3, 22, 3); */
 #endif
 printf("\n----------------------------------------\n");
 printf(" Promotion entière\n");
 printf("----------------------------------------\n");
 /* La promotion entière, à ne pas confondre avec la conversion automatique de type,
 fait que tous les types plus petits ou égaux à int (char, short, champs de bits,
 type énuméré) sont convertis (promus) en int ou unsigned int avant toute
 opération. */
 short sum(short a, short b)
 return a + b;
 /* équivaut à : return (int)a + (int)b;
 c'est à dire conversion de a en int + la conversion de b en int */
 /* Le compilateur peut émettre un avertissement du fait que le résultat int est
 converti en short pour être retourné, et cette conversion peut causer une perte
 de précision (par exemple si int a une largeur de 32 bits et short de 16 bits).
 La promotion se fait vers unsigned int lorsqu'un int ne peut pas représenter le
 type promu. */
 printf("\n----------------------------------------\n");
 printf(" Conversion automatique\n");
 printf("----------------------------------------\n");
 /* La conversion est un mécanisme qui permet de convertir implicitement les nombres
 dans le format le plus grand utilisé dans l'expression. */
 /* l'expression '2 / 3.' est de type double et vaudra effectivement deux tiers
 (0,666666...) */
 printf("2 / 3. = %f\n", 2 / 3.);
 /* 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 instructions sur les entiers (en supposant que 'a' soit un
 entier). */
 int a = 2;
 printf("2 * a / 3 = %d\n", 2 * a / 3);
 /* l'expression '2 / 3 * a' vaudra... toujours zéro ! */
 printf("2 / 3 * a = %d\n", 2 / 3 * a);
 /* 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. */
 printf("2 * a / 3. = %f\n", 2 * a / 3.);
 /* Enfin un cas où les promotions peuvent surprendre, c'est lorsqu'on mélange des
 entiers signés et non-signés, plus particulièrement dans les comparaisons.
 Considérez le code suivant : */
 unsigned long b = 23;
 signed char c = -23;
 printf("\nb = %lu\n", b);
 printf("c = %hhd\n", c);
 printf( "b %c c\n", b < c ? '<' : (b == c ? '=' : '>') );
 /* b < c ou b = c ou b > c
 b < c ?(alors) '<' :(sinon) (b == c ?(alors) '=' :(sinon) '>') */
 /* Ce code contient un effet de bord sibyllin(Enigmatique)
 De toute évidence b est supérieur à c dans cet exemple. Et pourtant, si ce code
 est exécuté sur certaines architectures, il affichera b < c, justement à cause de
 la conversion automatique.
 Tout d'abord dans la comparaison b < c, le type de b est « le plus grand », donc
 c est promu en unsigned long. C'est en fait ça le problème : -23 est une valeur
 négative, et la conversion d'une valeur négative en un type non signé se fait
 modulo la valeur maximale représentable par le type non signé + 1. Si la valeur
 maximale pour le type unsigned long est 2^32-1, alors -23 est converti modulo
 2^32, et devient 4 294 967 273 (soit 2^32-23). Dans ce cas là effectivement
 23 < 4 294 967 273, d'où ce résultat surprenant.
 // Dans ce cas présent, il faut effectuer soi-même le transtypage(la conversion) :
 printf( "b %c c\n", (long)b < c ? '<' : ((long)b == c ? '=' : '>') );
 /* on convertit b en long donc c sera lui converti automatiquement aussi en long
 long peut contenir des nombres positifs et négatifs
 donc -23 restera -23 en long et b sera plus grand que c */
 printf("\n----------------------------------------\n");
 printf(" Évaluation des expressions booléennes\n");
 printf("----------------------------------------\n");
 /* Le C ne possèdait 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.). */
 #if 0
 // exemple :
 int d;
 d = une_fonction();
 if (d)
 // ...
 // meme exemple en plus clair :
 int d;
 d = une_fonction();
 if (d != 0)
 // ...
 // autre exemple :
 int a = 0;
 int b = 2;
 if (a = b)
 // Le code qui suit sera toujours exécuté ...
 /* Dans cet exemple, il y a un seul caractère = entre a et b, donc on ne teste pas
 si a est égal à b, mais on affecte la valeur de 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. En effet, il est facile de faire
 une faute de frappe et d'oublier de taper un caratère =. Comme le code résultant
 est toujours valide au sens du C, cette erreur peut ne pas être vue
 immédiatement. Pour aider les développeurs à détecter ce genre d'erreurs, de
 nombreux compilateurs émettent un avertissement quand un tel code leur est
 présenté. Une manière de faire taire ces avertissements, une fois qu'on s'est
 assuré qu'il ne s'agit pas d'une erreur, est de parenthéser l'affectation: */
 if ((a = b))
 // ...
 // Ou mieux écrire
 a = b;
 if (a != 0)
 // ...
 /* Une autre technique classique, lorsqu'une comparaison fait intervenir une
 constante, est de mettre la constante à gauche. De cette manière, si la
 comparaison se transforme par mégarde en affectation, cela provoquera une erreur
 à la compilation : */
 if (0 == b)
 // Une instruction "0 = b" ne passerait pas
 /* 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");
 #endif
 return 0;