Programmation C/Préprocesseur
Le préprocesseur est un langage de macro qui est analysé, comme son nom l'indique, avant la compilation. En fait, c'est un langage complètement indépendant, il est même théoriquement possible de l'utiliser par dessus un autre langage que le C. Cette indépendance fait que le préprocesseur ignore totalement la structure de votre programme, les directives seront toujours évaluées de haut en bas.
Ces directives commencent toutes par le symbole dièse (codice_1), suivi d'un nombre quelconque de blancs (espace ou tabulation), suivi du nom de la directive en minuscule. Les directives doivent être déclarées sur une ligne dédiée. Les noms standards de directives sont :
Variables de substitution et macros.
Déclarations de constantes.
Les "variables de substitution" du préprocesseur fournissent un moyen simple de nommer des constantes. En effet :
permet de substituer "presque partout" dans le code source qui suit cette ligne la suite de caractères « CONSTANTE » par la "valeur". Plus précisément, la substitution se fait partout, à l'exception des caractères et des chaines de caractères. Par exemple, dans le code suivant :
printf("La constante TAILLE vaut %d\n", TAILLE);
La substitution se fera sur la deuxième occurrence de TAILLE, mais pas la première. Le préprocesseur transformera ainsi l'appel à codice_13 :
printf("La constante TAILLE vaut %d\n", 100);
Le préprocesseur procède à des traitements sur le code source, sans avoir de connaissance sur la structure de votre programme. Dans le cas des variables de substitution, il ne sait faire qu'un remplacement de texte, comme le ferait un traitement de texte. On peut ainsi les utiliser pour n'importe quoi, des constantes, des expressions, voire du code plus complexe ; le préprocesseur n'ira pas vérifier si la valeur de remplacement est une expression C ou non. Dans l'exemple précédent, nous avons utilisé codice_14 pour remplacer la constante 100, mais toute suite de caractères peut être définie. Par exemple:
printf(PREFIXE "%d\n", FORMULE);
Ici, l'appel à codice_13 sera transformé ainsi :
printf("erreur:" "%d\n", (1 + 1));
Une définition de constantes peut s'étendre sur plusieurs lignes. Pour cela, il faut que le dernier caractère de la ligne soit une barre oblique inverse ('\'). On peut ainsi définir une macro qui est remplacée par un bout de code plus complexe :
 default: \
 puts("Cas interdit."); \
 break;
Cette variable de substitution permet ainsi d'ajouter le cas par défaut à un codice_16.
Historiquement, les programmeurs avaient pour habitude d'utiliser des capitales pour distinguer les déclarations du préprocesseur et les minuscules pour les noms de symboles (fonctions, variables, champs, etc.) du compilateur. Ce n'est pas une règle à suivre impérativement, mais elle améliore la lisibilité des programmes.
Il est possible de définir plusieurs fois la même « CONSTANTE ». Le compilateur n'émettra un avertissement que si les deux valeurs ne concordent pas.
En fait, dans la section précédente, il était fait mention d'un mécanisme relativement similaire : les énumérations. On peut légitimement se demander ce que ces énumérations apportent en plus par rapport aux directives du préprocesseur. En fait, on peut essentiellement souligner que :
Déclarations automatiques.
Le langage C impose que le compilateur définisse un certain nombre de constantes. Sans énumérer toutes celles spécifiques à chaque compilateur, on peut néanmoins compter sur :
Extensions.
Chaque compilateur "peut" définir d'autres macro, pourvu qu'elles restent dans les conventions de nommage que la norme leur réserve. Les lister toutes ici est impossible et hors-sujet, mais on peut citer quelques unes, à titre d'exemple, que le lecteur pourra rencontrer régulièrement dans du code. Il va de soi que le fait qu'elles soient définies ou non, et leur éventuelle valeur, est entièrement dépendant du compilateur.
Déclaration de macros.
Une macro est en fait une constante qui peut prendre un certain nombre d'arguments.
Les arguments sont placés entre parenthèses après le nom de la macro sans espaces, par exemple :
La première macro prend deux arguments et « retourne » le maximum entre les deux. La deuxième est plus subtile, elle échange la valeur des deux arguments (qui doivent être des variables entières), sans passer par une variable temporaire, et ce avec le même nombre d'opérations.
La macro va en fait remplacer toutes les occurrences de la chaîne « MAX » et de ses arguments par « x > y ? x : y ». Ainsi, si on appelle la macro de cette façon :
printf("%d\n", MAX(4,6));
Elle sera remplacée par :
printf("%d\n", 4 > 6 ? 4 : 6);
Il faut bien comprendre qu'il ne s'agit que d'une substitution de texte, qui ne tient pas compte de la structure du programme. Considérez l'exemple suivant, qui illustre une erreur très classique dans l'utilisation des macros :
i = 2 * MAX(4,6); /* Sera remplacé par : i = 2 * 4 > 6 ? 4 : 6; */
L'effet n'est pas du tout ce à quoi on s'attendait. Il est donc important de bien parenthéser les expressions, justement pour éviter ce genre d'effet de bord. Il aurait mieux fallu écrire la macro codice_50 de la manière suivante :
En fait dès qu'une macro est composée d'autre chose qu'un élément atomique (un lexème, ou un "token") il est bon de le mettre entre parenthèses, notamment les arguments qui peuvent être des expressions utilisant des opérateurs ayant des priorités arbitraires.
Il est a noter que l'emploi systématique de parenthèses ne protège pas contre tous les effets de bord. En effet :
MAX(x++,y); /* Sera remplacé par : ((x++) > (y) ? (x++) : (y)) */
Du coup, x est incrémenté de 2 et non pas de 1. 
La qualité et la performance des compilateurs C modernes fait que l'utilisation de fonctions inline est le plus souvent préférable.
Suppression d'une définition.
Il arrive qu'une macro/constante soit déjà définie, mais qu'on aimerait quand même utiliser ce nom avec une autre valeur. Pour éviter un avertissement du préprocesseur, on doit d'abord supprimer l'ancienne définition, puis déclarer la nouvelle :
Cette directive supprime le symbole spécifié. À noter que même pour les macros avec arguments, il suffit juste de spécifier le nom de cette macro. Qui plus est, supprimer une variable de substitution inexistante ne provoquera aucune erreur.
Transformation en chaîne de caractères.
Le préprocesseur permet de transformer une expression en chaîne de caractères. Cette technique ne fonctionne donc qu'avec des macros ayant au moins un argument. Pour transformer n'importe quel argument de la macro en chaîne de caractères, il suffit de préfixer le nom de l'argument par le caractère dièse ('#'). Cela peut être utile pour afficher des messages de diagnostique très précis, comme dans l'exemple suivant :
if( (condition) == 0 ) \
 puts( "La condition '" #condition "' a échoué" ); \
 exit( 1 ); \
À noter qu'il n'existe pas de mécanisme simple pour transformer la "valeur" de la macro en chaîne de caractères. C'est le cas classique des constantes numériques qu'on aimerait souvent transformer en chaîne de caractères : le préprocesseur C n'offre malheureusement rien de vraiment pratique pour effectuer ce genre d'opération.
Concaténation d'arguments.
Il s'agit d'une facilité d'écriture pour simplifier les tâches répétitives. En utilisant l'opérateur codice_51, on peut concaténer deux expressions :
int version(variable); /* Déclare "int variable_v123;" */
Déclaration de macros à nombre variable d'arguments.
Ceci est une nouveauté du C99. La déclaration d'une macro à nombre variable d'arguments est en fait identique à une fonction, sauf qu'avec une macro on ne pourra pas traiter les arguments supplémentaires un à un. Ces paramètres sont en fait traités comme un tout, via le symbole codice_52. Exemple :
Il y a une restriction qui ne saute pas vraiment aux yeux dans cet exemple, c'est que les points de suspension doivent obligatoirement être remplacés par au moins un argument, ce qui n'est pas toujours très pratique. Malheureusement le langage C n'offre aucun moyen pour contourner ce problème pourtant assez répandu.
À noter, une extension du compilateur codice_19, qui permet de s'affranchir de cette limitation en rajoutant l'opérateur codice_51 à codice_52 :
/* Extension de gcc pour utiliser la macro sans argument */
Ces macros peuvent être utilisées par exemple pour des traitements autour de la fonction codice_13, voir par exemple
Variadic Macros .
Exemples.
Les macro du préprocesseur sont souvent utilisées dans des situations où une fonction, éventuellement codice_57, aurait le même effet, avec une meilleure robustesse. Cependant, il y a des usages pour lesquels les macros ne peuvent être remplacés par des fonctions.
Par exemple, pour afficher la taille des types C, on écrirait un programme comme le suivant :
int main(void)
 printf("sizeof(char) = %zd.\n", sizeof(char));
 printf("sizeof(short) = %zd.\n", sizeof(short));
 printf("sizeof(int) = %zd.\n", sizeof(int));
 return 0;
Écrire un tel programme peut être fatiguant, et il est très facile de faire des erreurs en recopiant les lignes. Ici, une fonction ne pourrait simplifier l'écriture. En effet, il faudrait passer à la fonction le nom du type, pour l'afficher dans la chaîne "sizeof(XXX) = ", et la taille. On devrait donc donner deux arguments à la fonction, pour l'appeler ainsi:
print_taille("char", sizeof(char));
Ce qui ne serait pas beaucoup plus simple, surtout que la fonction elle-même serait plus complexe (utilisation de fonctions codice_58 pour inclure le premier paramètre dans la chaîne de format). Ici, le préprocesseur fournit une solution beaucoup plus simple, à l'aide de l'opérateur codice_1 :
int main(void)
 PRINTF_SIZEOF(char);
 PRINTF_SIZEOF(short);
 PRINTF_SIZEOF(int);
 return 0;
Une technique similaire peut être utilisée pour conserver à l'affichage le nom de constantes ou d'énumérations :
enum etat_t { Arret, Demarrage, Marche, ArretEnCours };
void affiche_etat(etat_t etat)
 switch (etat)
 CASE_ETAT(Arret)
 CASE_ETAT(Demarrage)
 CASE_ETAT(Marche)
 CASE_ETAT(ArretEnCours)
 default:
 printf("Etat inconnu (%d).\n", etat);
 break;
Ces exemples sont tirés d'un message de Bill Godfrey sur comp.lang.c.
Compilation conditionnelle.
Les tests permettent d'effectuer de la compilation conditionnelle. La directive codice_60 permet de savoir si une constante ou une macro est définie. Chaque déclaration codice_60 doit obligatoirement se terminer par un codice_62, avec éventuellement une clause codice_63 entre. Un petit exemple classique :
/* S'utilise : debug( ("Variable x = %d\n", x) ); (double parenthésage) */
L'argument de la directive codice_60 doit obligatoirement être un symbole du préprocesseur. Pour utiliser des expressions un peu plus complexes, il y a la directive codice_65 (et codice_66, contraction de codice_67). Cette directive utilise des expressions semblable à l'instruction codice_68 : si l'expression évaluée est différente de zéro, elle sera considéré comme vraie, et comme fausse si l'expression s'évalue à 0.
On peut utiliser l'addition, la soustraction, la multiplication, la division, les opérateurs binaires (&, |, ^, ~, «, »), les comparaisons et les connecteurs logiques (&& et ||). Ces derniers sont évalués en circuit court comme leur équivalent C. Les opérandes possibles sont les nombres entiers et les caractères, ainsi que les macros elle-mêmes, qui seront remplacés par leur valeur.
À noter, l'opérateur spécial codice_69 qui permet de tester si une macro est définie (et qui renvoit donc un booléen). Exemple :
Cette directive est très pratique pour désactiver des pans entier de code sans rien y modifier. Il suffit de mettre une expression qui vaudra toujours zéro, comme :
/* Vous pouvez mettre ce que vous voulez ici, tout sera ignoré, même du code invalide */
Un autre avantage de cette technique, est que les directives de compilations peuvent être imbriquées, contrairement aux commentaires. Dans ce cas, s'il y avait eu d'autres directives codice_65 / codice_62 (correctement balancées), la clause codice_72 ne s'arrêterait pas au premier codice_62 rencontré, tandis que cela aurait été le cas avec des commentaires.
À noter qu'au niveau du préprocesseur il est impossible d'utiliser les opérateurs du C. Notamment l'opérateur codice_74, dont le manque aura fait grincer les dents à des générations de programmeurs, sera en fait interprété comme étant une macro. Il faut bien garder à l'esprit que le préprocesseur C est totalement indépendant du langage C.
Inclusion de fichiers.
Il s'agit d'une autre fonctionnalité massivement employée dans toute application C qui se respecte. Comme son nom l'indique, la directive codice_75 permet d'inclure in extenso le contenu d'un autre fichier, comme s'il avait été écrit en lieu et place de la directive. On peut donc voir ça comme de la factorisation de code, ou plutôt de "déclarations". On appelle de tels fichiers, des fichiers en-têtes ("header files") et on leur donne en général l'extension codice_76.
Il est rare d'inclure du code dans ces fichiers ("définitions" de variables ou de fonctions), principalement parce que ces fichiers sont destinés à être inclus dans plusieurs endroits du programme. Ces définitions seraient donc définies plusieurs fois, ce qui, dans le meilleur des cas, seraient du code superflu, et dans le pire, pourraient poser des problèmes à l'édition des liens.
On y place surtout des déclarations de type, macros, prototypes de fonctions relatif à un module. On en profite aussi pour documenter toutes les fonctions publiques, leurs paramètres, les valeurs renvoyées, les effets de bords, la signification des champs de structures et les pré/post conditions (quel état doit respecter la fonction avant/après son appel). Dans l'idéal on devrait pouvoir comprendre le fonctionnement d'un module, simplement en lisant son fichier en-tête.
La directive codice_75 prend en fait un argument : le nom du fichier que vous voulez inclure. Ce nom doit être soit mis entre guillemets doubles ou entre balises (< >). Cette différence affecte simplement l'ordre de recherche du fichier. Dans le premier cas, le fichier est recherché dans le répertoire où le fichier contenant la directive se trouve, puis le préprocesseur regarde à des endroits préconfigurés. Dans le second cas, il regardera seulement dans les endroits préconfigurés. Les endroits préconfigurés sont le répertoire "include" par défaut (par exemple codice_78, sous Unix) et ceux passés explicitement en paramètre au compilateur.
En fait l'argument de la directive codice_75 peut aussi être une macro, dont la valeur est un argument valide (soit une chaîne de caractères, soit un nom entre balises < et >) :
Exemple.
void affiche( void );
int main( void )
 affiche();
 return 0;
C'est comme si codice_82 avait été écrit :
void affiche( void );
int main( void )
 affiche();
 return 0;
Protection contre les inclusions multiples.
À noter un problème relativement récurrent avec les fichiers en-têtes : il s'agit des inclusions multiples. À mesure qu'une application grandit, il arrive fréquemment qu'un fichier soit inclus plusieurs fois à la suite d'une directive codice_75. Quand bien même les déclarations sont identiques, définir deux types avec le même nom n'est pas permis en C. On peut néanmoins s'en sortir avec cette technique issue de la nuit des temps :
/* Mettre toutes les déclarations ici */
C'est un problème tellement classique, que le premier réflexe lors de l'écriture d'un tel fichier est de rajouter ces directives. codice_84 est bien-sûr à adapter à chaque fichier, l'essentiel est que le nom soit unique dans toute l'application. Habituellement on utilise le nom du fichier, mis en majuscule, avec les caractères non-alphabétiques remplacés par des soulignés.
Avertissement et message d'erreur personnalisés.
Il peut être parfois utile d'avertir l'utilisateur que certaines combinaisons d'options sont dangereuses ou carrément invalides. Le préprocesseur dispose d'une directive pour effectuer cela:
Cette directive affichera en fait le message tel quel, qui peut évidemment contenir n'importe quoi, y compris des caractères réservés, sans qu'on ait besoin de le mettre entre guillemets (codice_85). Après émission du message, la compilation s'arrêtera.
À noter que certains compilateurs comme GCC proposent aussi la directive:
qui permet d'afficher un message sans arrêter la compilation.