Le préprocesseur en C
En C, toutes les lignes commençant par # sont traitées par le préprocesseur — un programme spécial exécuté avant la compilation. Le préprocesseur transforme le code source en un nouveau fichier C sans directives #, que le compilateur reçoit ensuite.
| Directive | Rôle | Exemple |
|---|---|---|
#include | Inclure un fichier d'en-tête | #include <stdio.h> |
#define | Définir une constante ou une macro | #define MAX 100 |
#undef | Supprimer une macro existante | #undef MAX |
#ifdef / #ifndef | Compilation conditionnelle | #ifndef HEADER_H |
#if / #elif / #else / #endif | Condition sur expressions constantes | #if VERSION >= 2 |
1. Directive #include
La directive #include copie le contenu d'un fichier d'en-tête dans le fichier courant avant compilation.
<> vs guillemets ""#include <stdio.h>— cherche dans les répertoires système (bibliothèques standard)#include "monfichier.h"— cherche d'abord dans le répertoire courant, puis dans les répertoires système
2. #define — Constantes
#define demande au préprocesseur de remplacer textuellement chaque occurrence du nom par l'expression donnée avant la compilation.
#define NOM valeur
/* Exemples */
#define MAX 100
#define PI 3.14159
#define VRAI 1
#define FAUX 0Exemple n°1 — Constante #define
#include <stdio.h>
#define MAX 100
int main(void)
{
/* Le préprocesseur remplace "max" par "100" avant compilation */
printf("max = %d\n", MAX);
return 0;
}max = 100
#define vs const #define est une substitution textuelle sans type. const int MAX = 100 définit une vraie variable typée, visible dans le débogueur et soumise aux règles de portée. En C moderne, préférer constpour les constantes simples.#define MAX 100 /* préprocesseur — sans type, sans portée */
const int MAX = 100; /* variable constante — typée, déboguable */3. Macros avec arguments
#define peut définir des macros paramétrées qui ressemblent à des appels de fonctions. Le préprocesseur substitue textuellement les arguments — sans vérification de type ni évaluation préalable.
#define NOM_MACRO(arg1, arg2, ...) expression
/* Bonne pratique : parenthéser chaque argument ET l'expression entière */
#define CARRE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))a. Macros génériques (sans vérification de type)
Exemple n°2 — Macro INCREMENT applicable à tout type
#include <stdio.h>
#define INCREMENT(x) ++x
int main(void)
{
char *ptr = "Bonjour";
int x = 5;
printf("%s\n", INCREMENT(ptr)); /* avance le pointeur d'un char */
printf("%d\n", INCREMENT(x)); /* incrémente l'entier */
return 0;
}onjour 6
b. Les arguments ne sont pas évalués avant l'expansion
Le préprocesseur substitue les arguments tels quels dans l'expression — sans les calculer au préalable. Cela peut produire des résultats inattendus si les arguments contiennent des opérateurs.
Exemple n°3 — Problème sans parenthèses
#include <stdio.h>
#define PRODUIT(a, b) a * b /* sans parenthèses — dangereux */
int main(void)
{
/* Expansion : 2+3 * 3+5 → 2 + 9 + 5 = 16 (pas 5×8=40) */
printf("%d\n", PRODUIT(2+3, 3+5));
return 0;
}16
Exemple n°4 — Correction avec parenthèses
#include <stdio.h>
#define PRODUIT(a, b) ((a) * (b)) /* parenthèses obligatoires */
int main(void)
{
/* Expansion : (2+3) * (3+5) = 5 × 8 = 40 */
printf("%d\n", PRODUIT(2+3, 3+5));
return 0;
}40
/* Incorrect */
#define CARRE(x) x * x
int r = 36 / CARRE(6); /* 36 / 6 * 6 = 36 (pas 1) */
/* Correct */
#define CARRE(x) ((x) * (x))
int r = 36 / CARRE(6); /* 36 / (6*6) = 1 */4. Opérateurs spéciaux des macros
a. Opérateur ## — Concaténation de jetons
L'opérateur ## fusionne deux jetons en un seul lors de l'expansion de la macro.
Exemple n°5 — Concaténation de jetons
#include <stdio.h>
#define CONCAT(a, b) a ## b
int main(void)
{
printf("%d\n", CONCAT(2, 3)); /* produit le jeton "23" */
return 0;
}23
## L'opérateur ##est utile pour générer des identifiants dynamiquement :#define DECLARE_VAR(type, n) type var##n
DECLARE_VAR(int, 1); /* équivaut à : int var1; */
DECLARE_VAR(int, 2); /* équivaut à : int var2; */b. Opérateur # — Conversion en chaîne (stringification)
Placer # devant un argument dans une macro le convertit en un littéral de chaîne.
Exemple n°6 — Conversion d'un jeton en chaîne
#include <stdio.h>
#define VERS_CHAINE(a) #a
/* Macro de débogage utile */
#define AFFICHER_VAR(x) printf(#x " = %d\n", x)
int main(void)
{
printf("%s\n", VERS_CHAINE(2)); /* affiche la chaîne "2" */
printf("%s\n", VERS_CHAINE(hello)); /* affiche la chaîne "hello" */
int compteur = 42;
AFFICHER_VAR(compteur); /* affiche "compteur = 42" */
return 0;
}2 hello compteur = 42
5. Macros sur plusieurs lignes
Pour définir une macro sur plusieurs lignes, utiliser \ en fin de chaque ligne sauf la dernière.
Exemple n°7 — Macro multiligne avec boucle
#include <stdio.h>
#define AFFICHER(i, limite) \
while (i < limite) \
{ \
printf("%d\n", i); \
i++; \
}
int main(void)
{
int i = 2;
AFFICHER(i, 8);
return 0;
}2 3 4 5 6 7
if/else sans accolades. Entourer le corps d'une macro avec do { … } while(0)garantit un comportement correct dans tous les contextes :#define ECHANGER(a, b) \
do { \
int tmp = (a); \
(a) = (b); \
(b) = tmp; \
} while (0)6. Macros avec arguments vs fonctions inline
Les macros paramétrées présentent des comportements inattendus que les fonctions inline évitent grâce à la vérification de type et à l'évaluation normale des arguments.
Exemple n°8 — Macro CARRE : résultat inattendu
#include <stdio.h>
#define CARRE(x) x * x /* sans parenthèses */
int main(void)
{
/* Expansion : 36 / 6 * 6 = 6 * 6 = 36 (pas 1) */
int r = 36 / CARRE(6);
printf("%d\n", r);
return 0;
}36
Exemple n°9 — Fonction inline : résultat correct
#include <stdio.h>
static inline int carre(int x) { return x * x; }
int main(void)
{
int r = 36 / carre(6); /* 36 / 36 = 1 — correct */
printf("%d\n", r);
return 0;
}1
| Critère | Macro #define | Fonction inline |
|---|---|---|
| Vérification de type | Non | Oui |
| Évaluation des arguments | Textuelle (peut être multiple) | Normale (une seule fois) |
| Débogage | Difficile | Facile |
| Portée | Globale (depuis la définition) | Soumise aux règles de portée C |
| Recommandation | Constantes simples, inclusions conditionnelles | Remplacer les macros avec arguments |
7. Macros prédéfinies
Le compilateur C fournit plusieurs macros prédéfinies utiles pour le débogage et les informations de compilation.
| Macro | Type | Contenu |
|---|---|---|
__FILE__ | char * | Nom du fichier source courant |
__LINE__ | int | Numéro de ligne courant |
__DATE__ | char * | Date de compilation ("Aug 27 2024") |
__TIME__ | char * | Heure de compilation ("12:44:21") |
__func__ | char * | Nom de la fonction courante (C99) |
Exemple n°10 — Macros prédéfinies et macro de débogage
#include <stdio.h>
/* Macro de débogage utilisant les macros prédéfinies */
#define DEBUG(msg) \
printf("[%s:%d] %s\n", __FILE__, __LINE__, msg)
int main(void)
{
printf("Fichier : %s\n", __FILE__);
printf("Date : %s\n", __DATE__);
printf("Heure : %s\n", __TIME__);
printf("Ligne : %d\n", __LINE__);
printf("Fonction : %s\n", __func__);
DEBUG("Programme démarré");
return 0;
}Fichier : prog.c Date : Aug 27 2024 Heure : 12:44:21 Ligne : 13 Fonction : main [prog.c:15] Programme démarré
8. #undef et compilation conditionnelle
a. #undef — Supprimer une macro
Exemple n°11 — Suppression d'une macro avec #undef
#include <stdio.h>
#define MAX 100
int main(void)
{
printf("MAX avant undef : %d\n", MAX);
#undef MAX /* MAX n'existe plus après cette ligne */
/* printf("MAX = %d\n", MAX); */ /* erreur : identifier non déclaré */
return 0;
}b. Garde d'inclusion (include guard)
Sans protection, inclure un fichier d'en-tête deux fois provoque des erreurs de redéclaration. Le motif include guard avec #ifndef résout ce problème.
/* fichier : monfichier.h */
#ifndef MONFICHIER_H
#define MONFICHIER_H
/* contenu du fichier d'en-tête */
void maFonction(int x);
#endif /* MONFICHIER_H *//* Code de débogage actif seulement si DEBUG est défini */
#ifdef DEBUG
printf("Valeur de x : %d\n", x);
#endif
/* Adaptation selon la plateforme */
#ifdef _WIN32
#define SEPARATEUR "\\"
#else
#define SEPARATEUR "/"
#endifRécapitulatif
| Élément | Syntaxe | Point clé |
|---|---|---|
| Constante | #define NOM val | Substitution textuelle sans type |
| Macro avec args | #define F(x) ((x)*(x)) | Toujours parenthéser args et expression |
| Stringification | #define S(x) #x | Convertit un jeton en chaîne littérale |
| Concaténation | #define C(a,b) a##b | Fusionne deux jetons en un |
| Multiligne | #define M(x) do { … } while(0) | Utiliser do…while(0) pour la sécurité |
| Suppression | #undef NOM | La macro n'existe plus après cette ligne |
| Include guard | #ifndef H … #endif | Évite les inclusions multiples |
| Macros prédéfinies | __FILE__, __LINE__… | Utiles pour le débogage |
| Alternative recommandée | Fonction inline | Remplace les macros avec arguments — typée et sûre |
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.