Zoals de naam al doet vermoeden zijn Preprocessors programma’s die onze broncode bewerken voordat deze wordt gecompileerd. Er zijn een aantal stappen betrokken tussen het schrijven van een programma en het uitvoeren van een programma in C / C++. Laten we deze stappen eens bekijken voordat we daadwerkelijk beginnen te leren over Preprocessors.
U kunt de tussenstappen zien in het bovenstaande diagram. De broncode die door programmeurs is geschreven, wordt opgeslagen in het bestand program.c. Dit bestand wordt vervolgens verwerkt door preprocessors en er wordt een uitgebreid broncodebestand gegenereerd met de naam program. Dit uitgebreide bestand wordt gecompileerd door de compiler en een objectcodebestand wordt gegenereerd met de naam program.obj. Tenslotte koppelt de linker dit object code bestand aan de object code van de bibliotheek functies om het uitvoerbare bestand program.exe te genereren.
Preprocessor programma’s bieden preprocessor directives die de compiler vertellen om de broncode te preprocesseren alvorens te compileren. Al deze preprocessor directives beginnen met een ‘#’ (hash) symbool. Het ‘#’ symbool geeft aan dat, welke instructie ook begint met #, naar het preprocessor programma gaat, en het preprocessor programma zal deze instructie uitvoeren. Voorbeelden van enkele preprocessor directives zijn: #include, #define, #ifndef enz. Onthoud dat # symbool alleen een pad aangeeft dat het naar de preprocessor zal gaan, en commando’s zoals include worden verwerkt door het preprocessor programma. Bijvoorbeeld, include zal extra code toevoegen aan je programma. We kunnen deze preprocessor directives overal in ons programma plaatsen.
Er zijn 4 hoofdtypen preprocessor directives:
- Macro’s
- File Inclusion
- Conditional Compilation
- Other directives
Laten we nu over elk van deze directives in detail meer te weten komen.
- Macro’s: Macro’s zijn een stukje code in een programma dat een bepaalde naam krijgt. Wanneer de compiler deze naam tegenkomt, vervangt de compiler de naam door het eigenlijke stukje code. De ‘#define’ richtlijn wordt gebruikt om een macro te definiëren. Laten we nu de macrodefinitie begrijpen met behulp van een programma:
#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;
}
Output:
01234
In het bovenstaande programma vervangt de compiler het woord LIMIT door 5. Het woord ‘LIMIT’ in de macrodefinitie wordt een macrotemplate genoemd en ‘5’ is macro-uitbreiding.
Note: Er staat geen puntkomma(‘;’) aan het eind van de macro definitie. Macro definities hebben geen puntkomma nodig om te eindigen.
Macro’s met argumenten: We kunnen ook argumenten aan macro’s doorgeven. Macro’s gedefinieerd met argumenten werken op dezelfde manier als functies. Laten we dit met een programma begrijpen:
#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;
}
Output:
Area of rectangle is: 50
Uit bovenstaand programma kunnen we opmaken dat, telkens wanneer de compiler AREA(l, b) in het programma tegenkomt, hij dit vervangt door het statement (l*b) . Niet alleen dit, de waarden die worden doorgegeven aan het macro sjabloon AREA(l, b) zullen ook worden vervangen door het statement (l*b). Daarom zal AREA(10, 5) gelijk zijn aan 10*5.
- Bestandsinclusie: Dit type preprocessor directive vertelt de compiler om een bestand op te nemen in het broncode programma. Er zijn twee soorten bestanden die door de gebruiker in het programma kunnen worden opgenomen:
- Header-bestand of standaardbestanden: Deze bestanden bevatten de definitie van voorgedefinieerde functies zoals printf(), scanf() enz. Deze bestanden moeten worden opgenomen om met deze functies te kunnen werken. Verschillende functies worden gedeclareerd in verschillende header bestanden. Bijvoorbeeld standaard I/O functies staan in het bestand ‘iostream’ terwijl functies die string operaties uitvoeren in het bestand ‘string’ staan.
Syntaxis:
- Header-bestand of standaardbestanden: Deze bestanden bevatten de definitie van voorgedefinieerde functies zoals printf(), scanf() enz. Deze bestanden moeten worden opgenomen om met deze functies te kunnen werken. Verschillende functies worden gedeclareerd in verschillende header bestanden. Bijvoorbeeld standaard I/O functies staan in het bestand ‘iostream’ terwijl functies die string operaties uitvoeren in het bestand ‘string’ staan.
#include< file_name >
waarbij file_name de naam is van het bestand dat moet worden opgenomen. De ‘<‘ en ‘>’ haakjes vertellen de compiler om het bestand te zoeken in de standaard directory.
- door de gebruiker gedefinieerde bestanden: Wanneer een programma erg groot wordt, is het een goede gewoonte om het in kleinere bestanden op te delen en op te nemen wanneer dat nodig is. Dit soort bestanden zijn door de gebruiker gedefinieerde bestanden. Deze bestanden kunnen worden opgenomen als:
#include"filename"
- Conditional Compilation: Conditional Compilation directives zijn type directives die helpen om een specifiek deel van het programma te compileren of om het compileren van een specifiek deel van het programma over te slaan op basis van een aantal voorwaarden. Dit kan worden gedaan met behulp van twee preprocessing commando’s ‘ifdef’ en ‘endif’.
Syntax:
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif
Als de macro met de naam ‘macroname’ is gedefinieerd, wordt het blok statements normaal uitgevoerd, maar als de macro niet is gedefinieerd, slaat de compiler dit blok statements gewoon over.
- Andere richtlijnen: Naast de bovenstaande directives zijn er nog twee directives die niet vaak worden gebruikt. Deze zijn:
- #undef-richtlijn: De #undef-richtlijn wordt gebruikt om een bestaande macro te dedefiniëren. Deze richtlijn werkt als volgt:
#undef LIMIT
Door deze instructie te gebruiken, wordt de bestaande macro LIMIT ongedefinieerd. Na deze instructie zal elke “#ifdef LIMIT”-instructie onwaar blijken te zijn.
- #pragma-richtlijn: Deze richtlijn is een richtlijn voor speciale doeleinden en wordt gebruikt om bepaalde functies in of uit te schakelen. Dit soort directives zijn compiler-specifiek, d.w.z. ze verschillen van compiler tot compiler. Sommige van de #pragma directives worden hieronder besproken:
- #pragma startup en #pragma exit: Deze directieven helpen ons om de functies op te geven die moeten worden uitgevoerd voor het opstarten van het programma( voordat de besturing naar main() gaat) en net voor het afsluiten van het programma (net voordat de besturing terugkeert van main()).
Note: Onderstaand programma zal niet werken met GCC compilers.
Kijk eens naar het onderstaande programma:
- #pragma startup en #pragma exit: Deze directieven helpen ons om de functies op te geven die moeten worden uitgevoerd voor het opstarten van het programma( voordat de besturing naar main() gaat) en net voor het afsluiten van het programma (net voordat de besturing terugkeert van 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;
}
Output:
Inside func1()Inside main()Inside func2()
De bovenstaande code zal de onderstaande uitvoer opleveren wanneer hij wordt uitgevoerd op GCC-compilers:
Inside main()
Dit gebeurt omdat GCC #pragma startup of exit niet ondersteunt. U kunt echter de onderstaande code gebruiken voor een vergelijkbare uitvoer op GCC-compilers.
#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: Deze richtlijn wordt gebruikt om de waarschuwingsberichten te verbergen die tijdens het compileren worden weergegeven.
We kunnen de waarschuwingen verbergen zoals hieronder wordt getoond:- #pragma warn -rvl: Deze directive verbergt de waarschuwingen die worden gegeven wanneer een functie die een waarde moet teruggeven, geen waarde teruggeeft.
- #pragma warn -par: Deze directive verbergt de waarschuwingen die worden gegeven wanneer een functie de parameters die worden doorgegeven niet gebruikt.
- #pragma warn -rch: Deze directive verbergt de waarschuwingen die worden gegeven wanneer een code onbereikbaar is. Bijvoorbeeld: alle code die wordt geschreven na de return-instructie in een functie is onbereikbaar.
Dit artikel is geschreven door Harsh Agarwal. Als je GeeksforGeeks leuk vindt en een bijdrage wilt leveren, kun je ook een artikel schrijven via contribute.geeksforgeeks.org of mail je artikel naar [email protected]. Zie je artikel verschijnen op de GeeksforGeeks hoofdpagina en help andere Geeks.