5.2. Les commandes du préprocesseur

Toute commande du préprocesseur commence :

Les commandes sont les suivantes :

5.2.1. Inclusion de fichier

L'inclusion de fichier permet de factoriser du texte commun à plusieurs autres fichiers (par exemple des déclarations de types, de constantes, de fonctions, etc.). Le texte commun est mis en général dans un fichier portant l'extension .h (pour « header », fichier d'en-tête de programme).

Syntaxe :

#include "fichier"
ou :
#include <fichier>

fichier est le nom du fichier à inclure. Lorsque son nom est entre guillemets, le fichier spécifié est recherché dans le répertoire courant (normalement le répertoire du programme). S'il est encadré de crochets, il est recherché d'abord dans les répertoires spécifiés en ligne de commande avec l'option -I, puis dans les répertoires du chemin de recherche des en-têtes du système (ces règles ne sont pas fixes, elles ne sont pas normalisées).

Le fichier inclus est traité lui aussi par le préprocesseur.

La signification de la ligne #include <stdio.h> au début de tous les programmes utilisant les fonctions scanf et printf devient alors claire. Si vous ouvrez le fichier stdio.h, vous y verrez la déclaration de toutes les fonctions et de tous les types de la librairie d'entrée - sortie standard. De même, les fonctions malloc et free sont déclarées dans le fichier d'en-tête stdlib.h et définies dans la librairie standard. L'inclusion de ces fichiers permet donc de déclarer ces fonctions afin de les utiliser.

5.2.2. Remplacement de texte

Syntaxe :

#define texte remplacement

texte est le texte à remplacer. Le texte de remplacement est remplacement. Il est facultatif (dans ce cas, c'est le texte vide). À chaque fois que texte sera rencontré, il sera remplacé par remplacement.

Cette commande servira à définir les constantes.

Exemple 5-1. Définition de constantes

#define VRAI 1
#define FAUX 0

VRAI sera remplacé systématiquement par 1 et FAUX par 0 dans la suite du texte.

Le préprocesseur définit un certain nombre de chaînes de remplacement automatiquement. Ce sont les suivantes :

Note : Si __FILE__, __DATE__, __TIME__ et __cplusplus sont bien des constantes pour un fichier donné, ce n'est pas le cas de __LINE__. En effet, cette dernière « constante » change bien évidemment de valeur à chaque ligne. On peut considérer qu'elle est redéfinie automatiquement par le préprocesseur à chaque début de ligne.

On fera par ailleurs une distinction bien nette entre les constantes littérales définies avec la directive #define du préprocesseur et les constantes définies avec le mot-clé const. En effet, les constantes littérales ne réservent pas de mémoire. Ce sont des valeurs immédiates, définies par le compilateur. En revanche, les variables de classe de stockage const ont malgré tout une place mémoire réservée. Il est possible, par un transtypage de pointeur, de modifier leur valeur (opération déconseillée et de surcroît certainement inutile), et elles peuvent être modifiées par l'environnement si elles sont de type volatile. Ce sont donc des variables accessibles en lecture seule plutôt que des constantes. On ne pourra jamais supposer qu'une variable ne change pas de valeur sous prétexte qu'elle a la classe de stockage const, alors qu'évidemment, une vraie constante déclarée avec la directive #define du préprocesseur conservera toujours sa valeur (pourvu qu'on ne la redéfinisse pas).

5.2.3. Définition d'un identificateur

Il est possible de déclarer un identificateur. Pour cela, on utilise aussi #define :

#define identificateur

Pour détruire l'identificateur, il faut utiliser #undef :

#undef identificateur

Lorsqu'un identificateur est défini, il est possible de faire des tests dessus (voir section suivante).

5.2.4. Suppression de texte

Syntaxe :

#ifdef identificateur
   ⋮
#endif

Le texte compris entre le #ifdef (c'est-à-dire « if defined ») et le #endif est laissé tel quel si l'identificateur identificateur est connu du préprocesseur. Sinon, il est supprimé. L'identificateur peut être déclaré en utilisant simplement la commande #define vue ci-dessus.

Il existe d'autres commandes conditionnelles :

#ifndef      (if not defined ...)
#elif        (sinon, si ... )
#if          (si ... )
qui permettent de faire des fichiers dont certaines parties ne seront pas compilées. C'est ce qu'on appelle la compilation conditionnelle. La directive #if attend en paramètre une expression constante. Le texte qui la suit est inclus dans le fichier si et seulement si cette expression est non nulle. Par exemple :

#if (__cplusplus==199711L)
   ⋮
#endif

permet d'inclure un morceau de code C++ strictement à la norme décrite dans le projet de norme du 2 décembre 1996.

Une autre application courante des directives de compilation est la suivante :

#ifndef DejaLa
#define DejaLa

Texte à n'inclure qu'une seule fois au plus.

#endif
qui permet d'éviter que le texte soit inclus plusieurs fois, à la suite de plusieurs appels de #include. En effet, au premier appel, DejaLa n'est pas connu du préprocesseur. Il est donc déclaré et le texte est inclus. Lors de tout autre appel ultérieur, DejaLa existe, et le texte est n'est pas inclus. Ce genre d'écriture se rencontre dans les fichiers d'en-tête, pour lesquels en général on ne veut pas qu'une inclusion multiple ait lieu.

5.2.5. Autres commandes

Le préprocesseur est capable d'effectuer d'autres actions que l'inclusion et la suppression de texte. Les directives qui permettent d'effectuer ces actions sont indiquées ci-dessous :