Programmation C source/opérateurs

Un livre de Wikibooks.
Aller à : Navigation, rechercher
Programmation C source
Programmation C++
Sommaire
Modifier ce modèle
#include <stdio.h>
 
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émentation
-               unaire          DG      opposé
(type)          unaire          DG      opérateur de transtypage (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 nombre de "char" de l'expression
                                        (souvent en octet mais pas toujours, mais
                                        sizeof(char) == 1 par définition, voir Caractères)
 
* / %           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 avec séquencement
||              binaire         GD      ou 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;
}