Les directives préprocesseur
Description du chapitre et des ses objectifs :
Sous ce nom pompeux se trouve un outils très utile pour tout programmeur. Les
directives préprocesseur sont la pour aider à gérer un projet tenant sur plus d'un fichier .c et pour automatiser certaines tâches. Vous pourrez même vous initier à la création d'un code portable gâce à ces directives.
Accéder directement à une des parties du cours :
Le concept
Alors qu'est-ce qu'une directive préprocesseur?
C'est en fait un ensemble d'instructions qui seront faites par le compilateur AVANT la compilation consistant essentiellement à remplacer du code écrit ou encore à gérer des interfaces (cf prochain chapitre). Ce genre d'opération s'effectuent donc en même temps que quand le compilateur enlève les commentaires du code.
Vous avez déjà rencontré ces directives, pour les repérer, elles commencent une ligne avec l'opérateur "#". C'est bon? Vous avez vu où? Et oui, au début de votre programme:
Nous allons donc commencer par là.
L'inclusion d'en-tête
L'une des directives principales que vous rencontrerez se nomme #include. Comme vous le savez, cette commande permet d'inclure des fonctions à utiliser dans votre programme. La définition est une peu plus spécifique que cela: Un .h contient un ensemble de déclarations, comme des prototypes de fonction, des structures ... L'inclusion permet donc de déclarer ces objets dans votre programme pour pouvoir les utiliser, comme si vous copiez le code écrit dans d'autres fichiers dans votre fichier .c principal.
Vous aurez plus de précision sur cet outil dans le chapitre suivant encore une fois
Les macros
Voici un outil très interressant! Le #define permet d'assigner à une chaîne, une chaine qui va la remplacer dans votre programme. Cette commande doit tenir par défaut sur une seule ligne. Vous pouvez bien sur utiliser autant de fois que vous voulez ce que vous avez "définit" c'est d'ailleur le but, et vous pouvez faire autant de define que vous souhaitez. Le define se fait généralement en dehors de toute fonction, conservez cette utilisation. Voici la syntaxe de cet outil:
#define <chaine_de_remplacement> <chaine_remplacée>
Et on ne ne présente plus, le traditionnel exemple:
#define NOMBRE_POMMES_MAX 540
int main(void)
{
printf("Le nombre de pommes max est= %d\n", NOMBRE_POMMES_MAX
);
printf("On peut faire ce qu'on veut avec: %d", NOMBRE_POMMES_MAX /
2);
return 0;
}
Le compilateur va se charger automatiquementde remplacer chaque occurence de NOMBRE_POMMES_MAX par 540. Comme vous pouvez le constatez, pas de ";" à la fin d'une directive préprocesseur, ce n'est pas une instruction ordinaire, c'est juste un remplacement de caractères. A la compilation voici ce que va devenir le programme:
Attention : NOMBRE_POMMES_MAX n'est pas une variable! C'est juste un remplacement au chiffre 540.
int main(void)
{
printf("Le nombre de pommes max est= %d\n",
540);
printf("On peut faire ce qu'on veut avec: %d",
540 /
2);
return 0;
}
Vous pouvez aussi définir des chaines de caractère:
#define QUELQUECHOSE "Quelquechose"
Bref, ce que vous voulez. Attention par contre à ne pas mettre d'espaces dans la chaîne de remplacement. Il de est de coutûme de mettre les noms des directives en majuscule pour éviter de les confondre avec des noms de variables.
Une autre utilisation interessant est la
macro-fonction. Vous pouvez créer une fonction qui grossira un peu la taille de l'executable mais qui ira beaucoup plus vite car ne prend pas la place d'une fonction dans la mémoire. On l'utilise pour faire des fonction dont on a besoin souvent. Par exemple, une fonction qui retourne le carré d'un nombre:
Encore une fois, la macro va se contenter de remplacer chaque occurrence de
CARRE(<nombre>
par
<nombre>*<nombre>.
Un autre exemple avec 2 arguments:
#define MAX(a,b) (a < b) ? b : a
Avec celle ci, vous aurez le maximum de deux nombres.
Si jamais vous avez besoin de sauter des lignes, il faut mettre à la fin de la ligne un antislash \
Un petit exemple:
#define ERROR printf( "Erreur rencontree" );\
La fin de la déclaration de la directive se fera alors à la fin de la deuxième ligne.
De plus, il y a beaucoup de choses prédéfinies par votre compilateur! Certaines sont très utiles:
__LINE__ est un entier qui est la ligne en cours du fichier
__FILE__ est une chaîne de caractères contenant le nom du fichier en cours
__TIME__ est une chaîne de caractères contenant l'heure de compilation (heure/minute/seconde)
__DATE__ est une chaîne de caractères contenant la date de la compilation (jour/mois/année)
Il en existe pas mal d'autres mais souvent spécifiques à chaque compilateur. Je vous laisse vous documenter.
Une dernière chose est la possibilité de définir quelquechose sans pour autant donner de valeur pour remplacer, par exemple:
#define FICHIER
L'utilité sera expliquée dans la partie suivante.
Information : Tout ce qui a été définit peut être indéfinit grace à la directive: #undef suivit du nom de ce qui a été définit.
Il est aussi possible d'effectuer des transformations sur les caractères utilisés dans les directives prépocesseur. Le plus fort étant l'opérateur "#" qui permet de transformer un nom en chaine de caractère.
Par exemple, si je prends cette directive:
#define print_name_valeur_int(X) printf("%s = %d", #x, x)
Pour une utilisation telle que celle-ci:
int exemple = 152;
print_name_valeur_int(exemple);
Affichera:
La raison est simple, le préprocesseur va remplacer les valeur sur la ligne
print_name_valeur_int(exemple) par:
printf("%s = %d", "exemple" , exemple )
Cette outil est assez puissant et utile au débugging
Un autre de la même sorte permet de faire la concaténation de 2 noms: "##".
Par exemple, cette directive ajoutera le préfixe "my_" à un nom:
Que vous pourrez utiliser de cette façon:
A vous de trouver l'utilité
La compilation conditionnelle
S'il y a une partie qui peut sembler ne pas être utile pour le moment, c'est bien la compilation conditionnelle. Pourtant c'est la clef d'un programme portable. Les directives permettent d'executer certaines parties de code suivant un système de condition simple.
On trouvera les directives:
#if //commence tout ce qui suit sera executé si la condition est vrai
#else // tout ce qui suit sera executé dans le cas contraire du if
#endif //définit la fin de tout bloc #if... #else
A cela s'ajoute des test de définition grace au test
defined(<nom>
. Voici un exemple:
#define CODE_WINDOWS
#if defined(CODE_WINDOWS)
/* on fait ce qu'on veut */
#else
/* ne sera jamais lu car CODE_WINDOWS est définit, donc on peut mettre ce qu'on veut dans ce bloc*/
Salut les gens! J
'écris ce que je veux sans commentaires
C'est sympa non? Même la coloration ne fait pas la différence.#endif
Il existe un raccourcis pour
#if defined() et même sa négation (si pas définit):
#ifdef NOM
/* faire des choses */
#ifndef NOM
/* faire des choses*/
Créer une erreur
Il existe une directive interessante permettant d'envoyer un message à afficher à votre compilateur, je vous présente: #error qui est suivit du message que vous souahitez.
Par exemple, il existe une macro nommée _WIN32 qui signifie que vous compilez sous windows. Vous pourrez par exemple utiliser le message d'erreur de cette façon:
#ifndef_WIN32
#error Code non portable, compilation impossible
#endif
Et vous voilà paré à certaines éventualitées, encore un outil indispensable pour un projet de grande envergure!
Prochain chapitre, une application concrête des directives préprocesseur avec la programmation modulaire!
Sommaire - Chapitre suivant
Nos rédacteurs et membres sont pour la plupart ouverts à des remarques constructives et servir à alerter le rédacteur du cours, des fautes éventuelles ou de propositions et nouvelles perspectives de cours etc ...
Pour ce faire cliquez ici
Postez vous aussi un commentaire à cette partie via le lien que voici