Préprocesseur C++ : Comment les directives façonnent votre code
Les préprocesseurs sont les directives qui donnent des instructions au compilateur pour pré-traiter l'information avant que la compilation réelle ne commence.
Toutes les directives de préprocesseur commencent par #, et seuls des caractères d'espacement peuvent apparaître avant une directive de préprocesseur sur une ligne. Les directives de préprocesseur ne sont pas des instructions C++, donc elles ne se terminent pas par un point-virgule (;).
Vous avez déjà vu une directive #include dans tous les exemples. Cette macro est utilisée pour inclure un fichier d'en-tête dans le fichier source.
Il y a un certain nombre de directives de préprocesseur supportées par C++ comme #include, #define, #if, #else, #line, etc. Voyons les directives importantes :
Le préprocesseur #define
La directive de préprocesseur #define crée des constantes symboliques. La constante symbolique est appelée un macro et la forme générale de la directive est :
#define nom_macro texte_remplacement
Quand cette ligne apparaît dans un fichier, toutes les occurrences subséquentes du macro dans ce fichier seront remplacées par texte_remplacement avant que le programme ne soit compilé. Par exemple :
#include <iostream>
using namespace std;
#define PI 3.14159
int main() {
cout << "Valeur de PI : " << PI << endl;
return 0;
}
Maintenant, faisons le pré-traitement de ce code pour voir le résultat en supposant que nous ayons le fichier de code source. Compilons-le avec l'option -E et redirigeons le résultat vers test.p. Maintenant, si vous vérifiez test.p, il aura beaucoup d'informations et au fond, vous trouverez la valeur remplacée comme suit :
$ gcc -E test.cpp > test.p
...
int main() {
cout << "Valeur de PI : " << 3.14159 << endl;
return 0;
}
Macros de type fonction
Vous pouvez utiliser #define pour définir un macro qui prendra des arguments comme suit :
#include <iostream>
using namespace std;
#define MIN(a,b) (((a)<(b)) ? a : b)
int main() {
int i, j;
i = 100;
j = 30;
cout << "Le minimum est " << MIN(i, j) << endl;
return 0;
}
Si nous compilons et exécutons le code ci-dessus, cela produirait le résultat suivant :
Le minimum est 30
Compilation conditionnelle
Il y a plusieurs directives qui peuvent être utilisées pour compiler sélectivement des portions du code source de votre programme. Ce processus s'appelle la compilation conditionnelle.
La construction conditionnelle du préprocesseur ressemble beaucoup à la structure de sélection 'if'. Considérez le code de préprocesseur suivant :
#ifndef NULL #define NULL 0 #endif
Vous pouvez compiler un programme pour des fins de débogage. Vous pouvez aussi activer ou désactiver le débogage en utilisant un seul macro comme suit :
#ifdef DEBUG cerr << "Variable x = " << x << endl; #endif
Cela cause l'instruction cerr à être compilée dans le programme si la constante symbolique DEBUG a été définie avant la directive #ifdef DEBUG. Vous pouvez utiliser l'instruction #if 0 pour commenter une portion du programme comme suit :
#if 0 code empêché de compiler #endif
Essayons l'exemple suivant :
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main() {
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr << "Trace : À l'intérieur de la fonction main" << endl;
#endif
#if 0
/* Cette partie est commentée */
cout << MKSTR(HELLO C++) << endl;
#endif
cout << "Le minimum est " << MIN(i, j) << endl;
#ifdef DEBUG
cerr << "Trace : Sortie de la fonction main" << endl;
#endif
return 0;
}
Si nous compilons et exécutons le code ci-dessus, cela produirait le résultat suivant :
Le minimum est 30 Trace : À l'intérieur de la fonction main Trace : Sortie de la fonction main
Les opérateurs # et ##
Les opérateurs de préprocesseur # et ## sont disponibles en C++ et ANSI/ISO C. L'opérateur # cause un jeton de texte_remplacement à être converti en une chaîne entourée de guillemets.
Considérez la définition de macro suivante :
#include <iostream>
using namespace std;
#define MKSTR(x) #x
int main() {
cout << MKSTR(HELLO C++) << endl;
return 0;
}
Si nous compilons et exécutons le code ci-dessus, cela produirait le résultat suivant :
HELLO C++
Voyons comment cela a fonctionné. Il est simple de comprendre que le préprocesseur C++ transforme la ligne :
cout << MKSTR(HELLO C++) << endl;
La ligne ci-dessus sera transformée en la ligne suivante :
cout << "HELLO C++" << endl;
L'opérateur ## est utilisé pour concaténer deux jetons. Voici un exemple :
#define CONCAT(x, y) x ## y
Quand CONCAT apparaît dans le programme, ses arguments sont concaténés et utilisés pour remplacer le macro. Par exemple, CONCAT(HELLO, C++) est remplacé par "HELLO C++" dans le programme comme suit :
#include <iostream>
using namespace std;
#define concat(a, b) a ## b
int main() {
int xy = 100;
cout << concat(x, y);
return 0;
}
Si nous compilons et exécutons le code ci-dessus, cela produirait le résultat suivant :
100
Voyons comment cela a fonctionné. Il est simple de comprendre que le préprocesseur C++ transforme :
cout << concat(x, y);
La ligne ci-dessus sera transformée en la ligne suivante :
cout << xy;
Macros C++ prédéfinis
C++ fournit un certain nombre de macros prédéfinis mentionnés ci-dessous :
| N° | Macro et description |
|---|---|
| 1 | __LINE__ Contient le numéro de ligne courant du programme lors de la compilation. |
| 2 | __FILE__ Contient le nom de fichier courant du programme lors de la compilation. |
| 3 | __DATE__ Contient une chaîne de la forme mois/jour/année qui est la date de traduction du fichier source en code objet. |
| 4 | __TIME__ Contient une chaîne de la forme heure:minute:seconde qui est l'heure à laquelle le programme a été compilé. |
Voyons un exemple pour tous les macros ci-dessus :
#include <iostream>
using namespace std;
int main() {
cout << "Valeur de __LINE__ : " << __LINE__ << endl;
cout << "Valeur de __FILE__ : " << __FILE__ << endl;
cout << "Valeur de __DATE__ : " << __DATE__ << endl;
cout << "Valeur de __TIME__ : " << __TIME__ << endl;
return 0;
}
Si nous compilons et exécutons le code ci-dessus, cela produirait le résultat suivant :
Valeur de __LINE__ : 6 Valeur de __FILE__ : test.cpp Valeur de __DATE__ : Feb 28 2011 Valeur de __TIME__ : 18:52:48
Langue C
- Maîtriser les directives de préprocesseur C# : Guide pratique
- Comprendre les types de données C++ : Fondamentaux, modificateurs et bonnes pratiques
- Maîtriser les opérateurs C++ : Guide complet avec exemples pratiques
- Commentaires C++ : Bonnes pratiques pour un code lisible et maintenable
- Comprendre les constructeurs C++ : Par défaut, paramétrés et de copie expliqués
- Maîtriser les templates de classe en C++ : Guide pratique
- Comprendre le préprocesseur C (CPP) : Comment il simplifie la compilation
- Langage C++ : Vue d'ensemble et caractéristiques clés
- Constantes et littéraux en C++ : Types, syntaxe et bonnes pratiques