Année universitaire 2002-2003
espace

Licence d'informatique
Module 4 - partie "C / shell"



Travaux dirigés 7 :

  • 1. Préprocesseur.
  • 2. Directives d'inclusion de fichiers.
  • 3. Directives de compilation conditionnelle.
  • 4. Directives de substitution symbolique.
  • 5. Exercice 1 : substitutions symboliques.
  • 6. Macro-instructions.
  • 7. Exercice 2 : erreurs de substitutions.
  • 8. Exercice 3 : programmes erronés.
  • 9. Macro-instructions (suite).
  • 10. Exercice 4 : macro-instructions.
  • 11. Exercice 5 : substitutions symboliques.

  • 1. Préprocesseur.

    Le "préprocesseur" est un programme qui effectue un pré-traitement, lors de la compilation d'un programme C, en supprimant dans un premier temps tous les commentaires, puis en traitant les "directives de compilation". Une fois cette étape préalable franchie, il envoie le programme C modifié au compilateur. Les directives de compilation, dans un programme C, commencent toutes par un caractère #. Elles ne se trouvent pas forcément en début de fichier. Les trois principaux types de directives sont :

    Certaines de ces directives ont déjà été utilisées lors des séances de travaux dirigés ou de travaux pratiques.

    2. Directives d'inclusion de fichiers.

    Syntaxe 1 :

    Exemple 1 :

    Syntaxe 2 :

    Exemple 2 :

    Remarque :

    3. Directives de compilation conditionnelle.

    Syntaxe 1 :

    Exemple 1 :

    ...
    #define TEST 20
    ...
    #if TEST > 5
    printf("OK\n");
    #elif TEST <= 0
    scanf("%d",&a);
    #else
    scanf("%d",&b);
    #endif
    ...
    ...
    printf("OK\n");
    ...

    Les comparaisons effectuées par le préprocesseur ne peuvent porter que sur des constantes entières (et pas sur des variables du programme, dont l'évaluation n'est possible qu'à l'exécution...).

    Syntaxe 2 :

    Exemple 2 :

    ...
    #define PARTIE_1
    ...
    #ifdef PARTIE_1
    printf("OK\n");
    #endif
    #ifdef PARTIE_2
    scanf("%d",&a);
    #endif
    ...
    ...
    printf("OK\n");
    ...

    Exemple 3 :

    ...
    #define DEBOGAGE
    ...
    #ifdef DEBOGAGE
    fprintf(stderr,"Ligne 1234 : a==%d - b==%d - c==%d\n",a,b,c);
    #endif
    ...
    #ifdef DEBOGAGE
    fprintf(stderr,"Ligne 2345 : a==%d - b==%d - c==%d\n",a,b,c);
    #endif
    ...

    Syntaxe 3 :

    Remarque :

    4. Directives de substitution symbolique.

    Syntaxe :

    Le préprocesseur remplace dans un programme C toutes les occurrences du symbole par son équivalent (éventuellement vide, comme cela a été vu dans le paragraphe précédent), excepté :

    Le préprocesseur tient à jour une "table des symboles", et réalise les substitutions sur le texte, de manière séquentielle, à l'aide de cette table. À chaque fois qu'il rencontre une directive de substitution symbolique (c'est-à-dire une ligne commençant par #define ou #undef), il met à jour la table des symboles.

    Exemple :


    Symbole


    Équivalent

    PI
    3.14159
    FAUX
    0
    VRAI
    1

    Symbole


    Équivalent

    FAUX
    1
    VRAI
    0

    Le symbole doit être composé seulement de lettres, de chiffres et du caractère _ et ne peut pas commencer par un chiffre (on préfère souvent n'utiliser comme lettres que des majuscules, bien que ceci n'ait aucun caractère obligatoire). L'équivalent doit être tapé sur une seule ligne. Si l'écriture de l'équivalent nécessite plusieurs lignes, il faut faire précéder la frappe de chaque caractère retour-chariot d'un caractère \

    5. Exercice 1.

    Comment faire pour ne pas modifier la séquence suivante, et la rendre néanmoins acceptable par un compilateur C (cette séquence n'est ni de l'Ada, ni du Pascal, ni du C) :

    ...
    if (i>0) then
    begin
    a=0;
    b=1
    end
    ...

    6. Macro-instructions.

    Il existe une forme paramétrée pour la substitution symbolique.

    Syntaxe :

    Les paramètres situés entre parenthèses qui suivent une occurrence de symbole dans le programme, sont identifiés par le préprocesseur à paramètre1, paramètre2, etc... L'équivalent est envoyé au compilateur par le préprocesseur, avec la même substitution des paramètres. On appelle cela une "macro-instruction" (ou "macro").

    Remarque :

    Exemple :

    7. Exercice 2.

    Donner la valeur affichée à l'écran quand on remplace, dans l'exemple précédent, la Ligne 4 du programme principal par :

    8. Exercice 3.

    Donner la cause de l'erreur (ou des erreurs) lors de la compilation des deux programmes suivants :


    programme1.c

    #define N 4
    void config_init(int mat[],int N)
    {

    ...
    }
    int main(void)
    {

    int tab[N+2];
    ...
    }



    programme2.c

    #include <stdlib.h>
    #define MIN(a,b) (((a)>(b))?(b):(a))
    #define PLUS(a,b) a+b
    #define MOINS(a,b) a-b
    #define INCR (a) a++
    int main(void)
    {
    int p,x=3,y=4;

    p=5*PLUS(x,y);
    p=MOINS(x+1,y+1);
    p=MIN(x++,y);
    INCR(x);
    exit(0);
    }

    9. Macro-instructions (suite).

    Autre exemple de macro-instruction :

    ...
    #define MESSAGE(n,ch) {int i;\
    for (i=0;i<n;i++) printf("%s\n",ch);\
    }
    ...

    L'emploi des macro-instructions doit faire l'objet d'une attention particulière. Pour éviter de nombreux problèmes (dus aux priorités des opérateurs), il est conseillé de parenthéser les paramètres de la macro-instruction. Il faut, de plus, éviter de rendre les programmes incompréhensibles par l'abus de directives #define

    Une macro-instruction permet d'optimiser le code compilé, en limitant le nombre d'appels à une fonction dans le programme exécutable. De plus, elle permet d'effectuer des actions sur des variables dont le type n'est pas connu a priori (dans l'exemple du paragraphe 6, la macro-instruction ABS peut calculer la valeur absolue d'un entier ou d'un flottant).

    Remarque :

    10. Exercice 4.

    1) Écrire les macro-instructions suivantes, qui sont effectivement définies, sous d'autres noms, dans le fichier /usr/include/ctype.h :

    2) Donner la ligne produite par le préprocesseur, en remplacement de la ligne suivante :

    11. Exercice 5.

    Qu'affichent les trois programmes suivants :


    programme3.c

    #include <stdio.h>
    #include <stdlib.h>
    #define F(a,b) (a)+(b)
    #define C1 x
    #define C2 y
    int main(void)
    {
    int x=2,y=3;
    printf("%d\n",F(C1,C2));
    exit(0);
    }


    programme4.c

    #include <stdio.h>
    #include <stdlib.h>
    #define bA a
    #define AB b
    #define aB a
    int main(void)
    {
    int a=1;
    int b=2;
    int bbB=3;
    int ABABB=4;
    printf("%d\n",ABABB);
    exit(0);
    }


    programme5.c

    #include <stdio.h>
    #include <stdlib.h>
    #define C1 0
    #define C2 C1
    #define C1 1
    int main(void)
    {
    int c=C2;
    printf("%d\n",c);
    exit(0);
    }


    Ces pages ont été réalisées par A. Crouzil, J.D. Durou et Ph. Joly.
    Pour tout commentaire, envoyer un mail à crouzil@irit.fr, à durou@irit.fr ou à Philippe.Joly@irit.fr.