Exood4 Studio - Video Game Development, Toulouse (France)
Exood4 Studios Exood4 Tutorials

   
L a n g a g e   C

L e    p r é p r o c e s s e u r






 Le rôle du préprocesseur est de préparer le fichier source en C pour le compilateur. Suivant l'architecture des machines, il peut être soit un programme indépendant, soit faire partie intégrante du compilateur, auquel cas la transformation du fichier source et la compilation se font en même temps.

 En fonction des directives qu'il rencontre au cours de la lecture du programme, le préprocesseur traite la source et génère un texte intermédiaire. Les directives sont éliminées après leur traitement. Cette source intermédiaire, qui est toujours du C, est ensuite compilée.

 Une directive est une ligne commençant par #. Elle se termine en fin de ligne, sauf si le dernier caractère est \ . Dans ce cas, elle continue sur la ligne suivante. Sa syntaxe est indépendante du reste du langage. En particulier, le préprocesseur ne reconnaît pas les mots clés. Une directive peut apparaître n'importe où dans le programme source.


Définir les symboles


 La directive #define permet de définir un symbole. Le préprocesseur substitue à partir de la ligne de sa définition et jusqu'à la fin du fichier source, toutes les occurrences du symbole reconnues à l'intérieur du programme par une chaîne de remplacement (à l'exception des occurrences contenues dans un commentaire ou dans une constante chaîne de caractères).


Exemple : #define symbole chaine_de_remplacement

 Le remplacement n'est qu'une simple substitution de chaînes de caractères et ne fait pas intervenir les règles syntaxiques du langage C. La chaîne de remplacement peut être vide. Un symbole est une entité virtuelle puisqu'il n'est pas vu par le compilateur. La chaîne de remplacement est elle même explorée pour y substituer d'éventuels symboles qui y seraient contenus.

#define BEGIN   {
#define END }
#define BJR_BSR BEGIN printf("bonjour\n"); \
 printf("bonsoir\n"); END

Ce qui permet d'écrire :

void main()
BEGIN
 ...
 if (...) BJR_BSR;
 ...
END

 De même pour définir des constantes :

#define PI 3.1415926535898
#define _2PI   2*PI

 Dans ce cas il est préférable d'utiliser le spécificateur de type const qui interdit la modification de la valeur de l'objet ainsi déclaré.

const double pi = 3.1415926535898;


Supprimer des symboles


 La directive #undef peut être utilisée pour supprimer la définition d'un symbole. Une fois supprimé, le symbole peut être redéfini.

#define PI 3.1415926535898
#undef PI
#define PI (355/113)


Inclure des fichiers


 La directive #include permet d'insérer un fichier. Elle est remplacée lors de la compilation par le contenu du fichier qui doit être un texte source en C.

#include <nom_de_fichier>

/* indique qu'il faut chercher le fichier dans les répertoires standards de travail du compilateur */


#include "nom_de_fichier"

/* indique qu'il faut d'abord chercher le fichier dans le répertoire contenant le fichier source qui est compilé, et seulement ensuite dans les répertoires standards. La première forme est utilisée pour se référer aux librairies standards (stdio.h, stdlib.h ...) */


Les fichiers en-têtes


 Chaque fonction doit être déclarée avant d'être utilisée. Ceci s'applique tout particulièrement aux fonctions de la librairie standard. Or déclarer chacune de ces fonctions avant de l'utiliser serait relativement pénible et source d'erreur. C'est pourquoi, pour éviter ce travail aux programmeurs, il existe des fichiers sources, appelés fichiers en-têtes (headers), qui contiennent ces déclarations. Chaque fichier en-tête correspond à une fonctionnalité :

 stdlib.h : traitements courants

 stdio.h : entrées/sorties

 string.h : manipulation de chaînes de caractères

 ctype.h : classification et conversion de caractères

 math.h : traitements mathématiques


 Les fichiers en-têtes standard contiennent aussi des définitions de symboles, de types, ... :

 time.h : définition des types et des fonctions de manipulation du temps

 limits.h : définition liées à l'architecture de la machine (tailles des types, valeurs extrêmes ...)


 Un programme de taille importante se décompose en général en plusieurs fichiers sources. Chacun de ces fichiers contient alors ses propres directives #include.

 Un problème peut se poser si nous avons simultanément plusieurs fichiers sources incluant le même fichier d'en-tête et le fichier en-tête en question qui contient une définition (par exemple : int a;). Le programme comporte dans ce cas plusieurs définitions d'une même variable globale (ici la variable entière a). Cette ambiguïté ne sera pas décelée à la compilation, puisque chaque fichier est compilé indépendamment des autres, mais cela provoquera des erreurs à l'édition des liens.

 Afin d'éviter ce genre de problème, il est conseillé de ne pas écrire dans un fichier en-tête des instructions générant du code. Autrement dit, on convient qu'un fichier en-tête peut contenir :
  • des déclarations : extern int a; int f(int,float);
  • des directives pour le préprocesseur : #define, #include
  • des définitions de types : struct complexe { float re, im; }; enum bool { faux, vrai };
  • des commentaires
 Il est déconseillé d'utiliser :
  • des définitions de variables : int a;
  • des définitions de fonctions : int plus (int a, int b) { return a+b; }


  Retour en haut de page Page suivante