> Maîtrisez le préprocesseur C : #define, #include, macros à arguments, compilation conditionnelle et l'astuce des X-macros pour vos énumérations.

*Source : https://coder-studio.com/tutoriels/preprocesseur-c-enumerations/*

---

[// C / C++](https://coder-studio.com/tutoriels/)

# Le préprocesseur C : macros et énumérations

Par **Kévin Papot** · publié le 7 mai 2026

Avant même que votre code C ne soit compilé, un outil discret le réécrit : le **préprocesseur**. Comprendre son fonctionnement, c’est démystifier les `#include`, écrire des macros propres, et découvrir des astuces puissantes comme les X-macros pour tenir synchronisées ses énumérations. Cet article prolonge l’un des tutoriels les plus commentés de l’histoire de Coder Studio.

## Une étape avant la compilation

Le préprocesseur effectue de la **substitution de texte** pure. Il ne comprend rien au C : il applique mécaniquement des directives commençant par `#`. Vous pouvez voir son résultat avec `gcc -E fichier.c`.

### #include et #define

```
#include <stdio.h>      // copie-colle le contenu du fichier
#define PI 3.14159      // remplace chaque PI par 3.14159
#define MAX 100
```

Règle d’hygiène : une constante `#define` est un simple texte, sans type ni portée. En C++ moderne, on lui préfère `constexpr`. Mais en C, les macros restent incontournables.

### Macros à arguments — et leurs pièges

```
#define CARRE(x) ((x) * (x))

int a = CARRE(3);        // 9
int b = CARRE(1 + 2);    // 9 grâce aux parenthèses !
```

Les parenthèses autour de chaque argument ET autour de l’expression entière sont **vitales**. Sans elles, `CARRE(1+2)` deviendrait `1+2*1+2 = 5`. C’est le piège n°1 des macros. Autre danger : les effets de bord — `CARRE(i++)` incrémente `i` deux fois.

### Compilation conditionnelle

```
#ifdef DEBUG
    printf("Valeur : %d\n", x);
#endif

#ifndef HEADER_H     // garde d'inclusion (include guard)
#define HEADER_H
// ... contenu du header ...
#endif
```

Les gardes d’inclusion évitent qu’un même header soit inclus deux fois. Le pragma non-standard mais universel `#pragma once` fait la même chose plus brièvement.

## L’astuce des X-macros pour les énumérations

Voici la technique qui faisait tout l’intérêt de l’article d’origine. Problème classique : garder synchronisées une `enum` et le tableau de chaînes qui la décrit. Oubliez d’ajouter une entrée dans l’un des deux, et tout se décale. La solution : une seule source de vérité.

```
// La liste, définie UNE seule fois
#define LISTE_COULEURS \
    X(ROUGE) X(VERT) X(BLEU)

// 1) on génère l'enum
#define X(nom) nom,
enum Couleur { LISTE_COULEURS };
#undef X

// 2) on génère le tableau de chaînes — impossible de désynchroniser
#define X(nom) #nom,
const char* noms[] = { LISTE_COULEURS };
#undef X
```

Ajoutez `X(JAUNE)` à la liste, et l’enum _comme_ le tableau se mettent à jour ensemble. L’opérateur `#` transforme un argument en chaîne (« stringification »), et `##` permet de concaténer des identifiants. Ces techniques, à manier avec parcimonie, révèlent la vraie puissance — et les dangers — du préprocesseur.

* * *

Le préprocesseur est la première des quatre étapes détaillées dans [notre guide du langage C](https://coder-studio.com/tutoriels/langage-c/). Pour passer aux abstractions plus sûres du C++, voyez [le RAII](https://coder-studio.com/tutoriels/raii-cpp/) et [le pattern Factory](https://coder-studio.com/tutoriels/design-pattern-factory-cpp/).
