Comme son nom l’indique les préprocesseurs sont des programmes qui traitent notre code source avant la compilation. Il y a un certain nombre d’étapes impliquées entre l’écriture d’un programme et l’exécution d’un programme en C / C++. Jetons un coup d’œil à ces étapes avant de commencer réellement à apprendre les Préprocesseurs.
Vous pouvez voir les étapes intermédiaires dans le diagramme ci-dessus. Le code source écrit par les programmeurs est stocké dans le fichier program.c. Ce fichier est ensuite traité par les préprocesseurs et un fichier de code source étendu est généré nommé program. Ce fichier étendu est compilé par le compilateur et un fichier de code objet nommé program.obj est généré. Enfin, le linker lie ce fichier de code objet au code objet des fonctions de la bibliothèque pour générer le fichier exécutable programme.exe.
Les programmes préprocesseurs fournissent des directives de préprocesseur qui indiquent au compilateur de prétraiter le code source avant de le compiler. Toutes ces directives de préprocesseur commencent par un symbole ‘#’ (dièse). Le symbole ‘#’ indique que, quelle que soit l’instruction commençant par #, elle est envoyée au programme du préprocesseur, qui l’exécutera. Voici quelques exemples de directives du préprocesseur : #include, #define, #ifndef etc. Rappelez-vous que le symbole # ne fournit qu’un chemin d’accès au préprocesseur, et que les commandes telles que include sont traitées par le préprocesseur. Par exemple, include inclura du code supplémentaire dans votre programme. Nous pouvons placer ces directives de préprocesseur n’importe où dans notre programme.
Il existe 4 types principaux de directives de préprocesseur :
- Macros
- Inclusion de fichiers
- Compilation conditionnelle
- Autres directives
Découvrons maintenant chacune de ces directives en détail.
- Macros : Les macros sont un morceau de code dans un programme auquel on donne un certain nom. Chaque fois que ce nom est rencontré par le compilateur, celui-ci remplace le nom par le morceau de code réel. La directive ‘#define’ est utilisée pour définir une macro. Comprenons maintenant la définition d’une macro à l’aide d’un programme :
#include <iostream>
#define LIMIT 5
int
main()
{
for
(
int
i = 0; i < LIMIT; i++) {
std::cout << i <<
"\n"
;
}
return
0;
}
#include <stdio.h>
#define LIMIT 5
int
main()
{
for
(
int
i = 0; i < LIMIT; i++) {
printf
(
"%d \n"
,i);
}
return
0;
}
Sortie :
01234
Dans le programme ci-dessus, lorsque le compilateur exécute le mot LIMIT, il le remplace par 5. Le mot ‘LIMIT’ dans la définition de la macro est appelé modèle de macro et ‘5’ est une expansion de macro.
Note : Il n’y a pas de point-virgule(‘;’) à la fin de la définition de la macro. Les définitions de macros n’ont pas besoin de point-virgule pour se terminer.
Macros avec des arguments : On peut aussi passer des arguments aux macros. Les macros définies avec des arguments fonctionnent de manière similaire aux fonctions. Comprenons cela avec un programme :
#include <iostream>
#define AREA(l, b) (l * b)
int
main()
{
int
l1 = 10, l2 = 5, area;
area = AREA(l1, l2);
std::cout <<
"Area of rectangle is: "
<< area;
return
0;
}
#include <stdio.h>
#define AREA(l, b) (l * b)
int
main()
{
int
l1 = 10, l2 = 5, area;
area = AREA(l1, l2);
printf
(
"Area of rectangle is: %d"
, area);
return
0;
}
Sortie :
Area of rectangle is: 50
On peut voir dans le programme ci-dessus que, chaque fois que le compilateur trouve AREA(l, b) dans le programme, il le remplace par l’instruction (l*b) . Non seulement cela, mais les valeurs passées au modèle de macro AREA(l, b) seront également remplacées dans l’instruction (l*b). Par conséquent, AREA(10, 5) sera égal à 10*5.
- Inclusion de fichier : Ce type de directive du préprocesseur indique au compilateur d’inclure un fichier dans le programme du code source. Il existe deux types de fichiers qui peuvent être inclus par l’utilisateur dans le programme :
- Fichier d’en-tête ou fichiers standards : Ces fichiers contiennent la définition de fonctions prédéfinies comme printf(), scanf() etc. Ces fichiers doivent être inclus pour travailler avec ces fonctions. Différentes fonctions sont déclarées dans différents fichiers d’en-tête. Par exemple, les fonctions d’E/S standard sont dans le fichier ‘iostream’ alors que les fonctions qui effectuent des opérations sur les chaînes de caractères sont dans le fichier ‘string’.
Syntaxe :
- Fichier d’en-tête ou fichiers standards : Ces fichiers contiennent la définition de fonctions prédéfinies comme printf(), scanf() etc. Ces fichiers doivent être inclus pour travailler avec ces fonctions. Différentes fonctions sont déclarées dans différents fichiers d’en-tête. Par exemple, les fonctions d’E/S standard sont dans le fichier ‘iostream’ alors que les fonctions qui effectuent des opérations sur les chaînes de caractères sont dans le fichier ‘string’.
#include< file_name >
où nom_du_fichier est le nom du fichier à inclure. Les parenthèses ‘<‘ et ‘>’ indiquent au compilateur de rechercher le fichier dans le répertoire standard.
- fichiers définis par l’utilisateur : Lorsqu’un programme devient très volumineux, il est bon de le diviser en fichiers plus petits et de les inclure chaque fois que nécessaire. Ces types de fichiers sont des fichiers définis par l’utilisateur. Ces fichiers peuvent être inclus comme :
#include"filename"
- Compilation conditionnelle : Les directives de compilation conditionnelle sont des types de directives qui aident à compiler une partie spécifique du programme ou à sauter la compilation d’une partie spécifique du programme en fonction de certaines conditions. Cela peut être fait avec l’aide de deux commandes de prétraitement ‘ifdef’ et ‘endif’.
Syntaxe :
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif
Si la macro dont le nom est ‘macroname’ est définie alors le bloc d’instructions s’exécutera normalement mais si elle n’est pas définie, le compilateur sautera simplement ce bloc d’instructions.
- Autres directives : En dehors des directives ci-dessus, il existe deux autres directives qui ne sont pas couramment utilisées. Ce sont :
- La directive #undef : La directive #undef est utilisée pour annuler la définition d’une macro existante. Cette directive fonctionne comme suit :
#undef LIMIT
L’utilisation de cette déclaration annulera la définition de la macro existante LIMIT. Après cette déclaration, chaque déclaration « #ifdef LIMIT » sera évaluée à false.
- Directive #pragma : Cette directive est une directive à usage spécial et est utilisée pour activer ou désactiver certaines fonctionnalités. Ce type de directives est spécifique au compilateur, c’est-à-dire qu’elles varient d’un compilateur à l’autre. Certaines des directives #pragma sont présentées ci-dessous :
- #pragma startup et #pragma exit : Ces directives nous aident à spécifier les fonctions qui doivent être exécutées avant le démarrage du programme( avant que le contrôle ne passe à main()) et juste avant la sortie du programme (juste avant que le contrôle ne revienne de main()).
Note : Le programme ci-dessous ne fonctionnera pas avec les compilateurs GCC.
Regardez le programme ci-dessous :
- #pragma startup et #pragma exit : Ces directives nous aident à spécifier les fonctions qui doivent être exécutées avant le démarrage du programme( avant que le contrôle ne passe à main()) et juste avant la sortie du programme (juste avant que le contrôle ne revienne de main()).
#include <bits/stdc++.h>
using
namespace
std;
void
func1();
void
func2();
#pragma startup func1
#pragma exit func2
void
func1()
{
cout <<
"Inside func1()\n"
;
}
void
func2()
{
cout <<
"Inside func2()\n"
;
}
int
main()
{
void
func1();
void
func2();
cout <<
"Inside main()\n"
;
return
0;
}
#include <stdio.h>
void
func1();
void
func2();
#pragma startup func1
#pragma exit func2
void
func1()
{
printf
(
"Inside func1()\n"
);
}
void
func2()
{
printf
(
"Inside func2()\n"
);
}
int
main()
{
void
func1();
void
func2();
printf
(
"Inside main()\n"
);
return
0;
}
Sortie :
Inside func1()Inside main()Inside func2()
Le code ci-dessus produira la sortie suivante lorsqu’il sera exécuté sur les compilateurs GCC :
Inside main()
Cela se produit parce que GCC ne supporte pas #pragma startup ou exit. Cependant, vous pouvez utiliser le code ci-dessous pour une sortie similaire sur les compilateurs GCC.
#include <stdio.h>
void
func1();
void
func2();
void
__attribute__((constructor)) func1();
void
__attribute__((destructor)) func2();
void
func1()
{
printf
(
"Inside func1()\n"
);
}
void
func2()
{
printf
(
"Inside func2()\n"
);
}
int
main()
{
printf
(
"Inside main()\n"
);
return
0;
}
- #pragma warn Directive : Cette directive permet de masquer les messages d’avertissement qui sont affichés lors de la compilation.
Nous pouvons cacher les avertissements comme indiqué ci-dessous :- #pragma warn -rvl : Cette directive cache les avertissements qui sont soulevés lorsqu’une fonction qui est censée retourner une valeur ne retourne pas une valeur.
- #pragma warn -par : Cette directive masque les avertissements qui sont émis lorsqu’une fonction n’utilise pas les paramètres qui lui sont passés.
- #pragma warn -rch : Cette directive cache les avertissements qui sont soulevés lorsqu’un code est inaccessible. Par exemple : tout code écrit après l’instruction return dans une fonction est inaccessible.
Cet article a été rédigé par Harsh Agarwal. Si vous aimez GeeksforGeeks et souhaitez contribuer, vous pouvez également écrire un article en utilisant contribute.geeksforgeeks.org ou envoyer votre article par courrier à [email protected]. Voyez votre article apparaître sur la page principale de GeeksforGeeks et aidez d’autres Geeks.