Macros et pré-processeurs en C

27 Aug 2019 27 Aug 2019 10233 vues ESSADDOUKI Mostafa 9 min de lecture

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.

Définition — Préprocesseur Le préprocesseur est la première phase de la compilation C. Il effectue des substitutions textuelles dans le code source : inclusion de fichiers, remplacement de macros, compilation conditionnelle. Son travail est purement textuel — il ne comprend pas la sémantique du C.
DirectiveRôleExemple
#includeInclure un fichier d'en-tête#include <stdio.h>
#defineDéfinir une constante ou une macro#define MAX 100
#undefSupprimer une macro existante#undef MAX
#ifdef / #ifndefCompilation conditionnelle#ifndef HEADER_H
#if / #elif / #else / #endifCondition 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.

Crochets <> 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.

   
Syntaxe — Constante symbolique C
#define NOM valeur

/* Exemples */
#define MAX   100
#define PI    3.14159
#define VRAI  1
#define FAUX  0

Exemple 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;
}
Sortie
max = 100
Astuce — #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.


Syntaxe — Macro avec arguments C
#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;
}
Sortie
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;
}
Sortie
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;
}
Sortie
40
Danger — Macro sans parenthèses : priorité des opérateursSans parenthèses autour des arguments et de l'expression, les priorités des opérateurs peuvent produire des résultats incorrects. Règle absolue : toujours entourer chaque argument et l'expression complète :
/* 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;
}
Sortie
23
Astuce — Générer des noms de variables avec ## 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;
}
Sortie
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;
}
Sortie
2
3
4
5
6
7
Attention — Macros multiligne et instructions composées Une macro contenant plusieurs instructions peut poser des problèmes avec 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;
}
Sortie
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;
}
Sortie
1
CritèreMacro #defineFonction inline
Vérification de typeNonOui
Évaluation des argumentsTextuelle (peut être multiple)Normale (une seule fois)
DébogageDifficileFacile
PortéeGlobale (depuis la définition)Soumise aux règles de portée C
RecommandationConstantes simples, inclusions conditionnellesRemplacer 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.

MacroTypeContenu
__FILE__char *Nom du fichier source courant
__LINE__intNumé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;
}
Sortie
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.


Syntaxe — Include guard C
/* fichier : monfichier.h */
#ifndef MONFICHIER_H
#define MONFICHIER_H

/* contenu du fichier d'en-tête */
void maFonction(int x);

#endif   /* MONFICHIER_H */
Compilation conditionnelle — cas d'usage courants
/* 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 "/"
#endif

Récapitulatif

ÉlémentSyntaxePoint clé
Constante#define NOM valSubstitution textuelle sans type
Macro avec args#define F(x) ((x)*(x))Toujours parenthéser args et expression
Stringification#define S(x) #xConvertit un jeton en chaîne littérale
Concaténation#define C(a,b) a##bFusionne deux jetons en un
Multiligne#define M(x) do { … } while(0)Utiliser do…while(0) pour la sécurité
Suppression#undef NOMLa 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éeFonction inlineRemplace 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.