Introduction et principes fondamentaux
Au sein de la chaîne de compilation GNU/Linux, le préprocesseur cpp intervient dès la toute première phase pour transformer le code source avant son passage à l’analyseur syntaxique. Ce guide pratique détaille le fonctionnement de cet utilitaire sous Ubuntu, en mettant l’accent sur l’évaluation des macros, l’injection dynamique de directives conditionnelles et l’arborescence d’inclusion des fichiers d’en-tête pour un débogage efficace de vos projets en langage C.
Vérification des prérequis et installation de cpp sous Ubuntu
Afin d’initialiser notre environnement de travail avec l’ensemble des dépendances nécessaires, nous déployons la suite GCC au moyen de la commande apt-get update -qq && apt-get install -y gcc.

L’interrogation de l’emplacement du binaire système s’effectue ensuite en lançant which cpp.
Le retour /usr/bin/cpp confirme la présence de l’exécutable dans l’arborescence standard des utilitaires système d’Ubuntu.

Pour examiner la version active du préprocesseur GNU et son environnement de compilation, nous invoquons cpp --version.
Le système valide ici l’exploitation de la mouture 13.3.0, prête pour l’analyse syntaxique préliminaire.
⚙️ [Options fondamentales]
-D name : Déclare un symbole de macro en ligne de commande pour piloter la compilation conditionnelle ou substituer des constantes.-U name : Révoque une macro préalablement déclarée ou intégrée par défaut au compilateur.-I dir : Ajoute un répertoire personnalisé à la liste des chemins explorés lors de la directive #include.-dM : Extrait la totalité des macros actuellement actives et définies au cours du prétraitement.-dD : Conserve les directives #define dans le flux de sortie aux côtés du code préprocessé.-P : Supprime les annotations de synchronisation de numéros de lignes (#line) pour épurer la sortie.-v : Affiche le déroulement détaillé du préprocesseur ainsi que l’ordre des répertoires d’inclusion par défaut.

Expansion de macros et analyse de code avec cpp
Pour expérimenter sans impacter d’autres éléments du système, nous créons un espace de travail dédié que nous rejoignons immédiatement à l’aide de mkdir -p ~/cpp_practice && cd ~/cpp_practice.

Nous générons un extrait de code C combinant une constante textuelle et une macro fonctionnelle avec printf '#define BUFFER_SIZE 1024\n#define SQUARE(x) ((x) * (x))\nint limit = BUFFER_SIZE;\nint area = SQUARE(5);\n' > macro_test.c.

L’évaluation des substitutions est ensuite déclenchée par la commande cpp macro_test.c output.i, qui stocke le résultat prétraité dans le fichier intermédiaire output.i.
L’ensemble des directives est alors résolu pour produire le code source développé.

Pour visualiser uniquement les instructions C résultantes en masquant les marqueurs techniques, nous exécutons cat output.i | grep -v '^#'.
La constante BUFFER_SIZE a cédé sa place à 1024, tandis que l’appel SQUARE(5) s’est transformé en l’expression mathématique développée ((5) * (5)).

Contrôle de la compilation conditionnelle via les options -D et -U
Nous nous repositionnons dans le répertoire d’expérimentation en saisissant cd ~/cpp_practice.

Nous créons un fichier source exploitant des embranchements #ifdef grâce à la commande printf '#ifdef DEBUG\nconst char *status = "DEBUG_ENABLED";\n#else\nconst char *status = "PRODUCTION";\n#endif\n' > condition_test.c.

Pour forcer l’activation du bloc de test sans modifier le fichier source, nous lançons cpp -DDEBUG -P condition_test.c.
La définition externe de la macro DEBUG oriente le choix vers la première branche, restituant fidèlement l’assignation const char *status = "DEBUG_ENABLED";.

À l’inverse, l’annulation explicite du symbole s’effectue via cpp -UDEBUG -P condition_test.c.
Le préprocesseur bascule alors automatiquement sur la section #else, confirmant la prise en compte du profil par défaut avec const char *status = "PRODUCTION";.

Gestion des chemins d’en-tête et filtrage des marqueurs (-I, -P)
Nous confirmons notre présence dans l’espace de test en lançant à nouveau cd ~/cpp_practice.

Afin d’illustrer la gestion des bibliothèques locales, nous isolons nos fichiers d’en-tête dans un sous-dossier dédié via mkdir -p custom_headers.

Nous y définissons une information de version logicielle à l’aide de l’instruction echo '#define APP_VERSION "1.0.0"' > custom_headers/version.h.

Le programme principal faisant référence à cette définition externe est généré au moyen de printf '#include "version.h"\nconst char *ver = APP_VERSION;\n' > main_app.c.

Enfin, nous indiquons explicitement le dossier de recherche tout en purgeant les métadonnées de lignes via cpp -I./custom_headers -P main_app.c.
Grâce au commutateur -I, le préprocesseur localise immédiatement version.h, tandis que -P assure l’affichage d’un code parfaitement nettoyé ne contenant que la ligne résolue const char *ver = "1.0.0";.

L’utilisation ciblée de la commande cpp sous Ubuntu offre une visibilité directe sur les mécanismes internes de transformation du code C. Ce contrôle pas à pas s’avère précieux pour déceler les pièges d’expansion de macros complexes et diagnostiquer les erreurs de compilation en amont.