Come suggerisce il nome, i preprocessori sono programmi che elaborano il nostro codice sorgente prima della compilazione. Ci sono una serie di passi coinvolti tra la scrittura di un programma e l’esecuzione di un programma in C / C++. Diamo un’occhiata a questi passi prima di iniziare effettivamente ad imparare i preprocessori.
Puoi vedere i passi intermedi nel diagramma sopra. Il codice sorgente scritto dai programmatori viene memorizzato nel file program.c. Questo file viene poi elaborato dai preprocessori e viene generato un file di codice sorgente espanso chiamato program. Questo file espanso viene compilato dal compilatore e viene generato un file di codice oggetto chiamato program.obj. Infine, il linker collega questo file di codice oggetto al codice oggetto delle funzioni di libreria per generare il file eseguibile program.exe.
I programmi di preprocessore forniscono direttive di preprocessore che dicono al compilatore di preprocessare il codice sorgente prima della compilazione. Tutte queste direttive di preprocessore iniziano con un simbolo ‘#’ (hash). Il simbolo ‘#’ indica che, qualunque dichiarazione inizi con #, sta andando al programma di preprocessore, e il programma di preprocessore eseguirà questa dichiarazione. Esempi di alcune direttive del preprocessore sono: #include, #define, #ifndef ecc. Ricorda che il simbolo # fornisce solo un percorso che andrà al preprocessore, e comandi come include sono processati dal programma di preprocessore. Per esempio, include includerà codice extra al tuo programma. Possiamo mettere queste direttive del preprocessore ovunque nel nostro programma.
Ci sono 4 tipi principali di direttive di preprocessore:
- Macro
- Inclusione di file
- Compilazione condizionata
- Altre direttive
Impariamo ora a conoscere ciascuna di queste direttive in dettaglio.
- Macro: Le macro sono un pezzo di codice in un programma a cui viene dato un nome. Ogni volta che questo nome viene incontrato dal compilatore, il compilatore sostituisce il nome con l’effettivo pezzo di codice. La direttiva “#define” è usata per definire una macro. Capiamo ora la definizione della macro con l’aiuto di un 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
Nel programma di cui sopra, quando il compilatore esegue la parola LIMIT la sostituisce con 5. La parola ‘LIMIT’ nella definizione della macro è chiamata template macro e ‘5’ è l’espansione della macro.
Nota: Non c’è il punto e virgola(‘;’) alla fine della definizione della macro. Le definizioni di macro non hanno bisogno di un punto e virgola alla fine.
Macro con argomenti: Possiamo anche passare argomenti alle macro. Le macro definite con argomenti funzionano in modo simile alle funzioni. Capiamo questo con un programma:
#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
Possiamo vedere dal programma di cui sopra che, ogni volta che il compilatore trova AREA(l, b) nel programma lo sostituisce con la dichiarazione (l*b). Non solo questo, anche i valori passati al template macro AREA(l, b) saranno sostituiti nell’istruzione (l*b). Pertanto AREA(10, 5) sarà uguale a 10*5.
- Inclusione di file: Questo tipo di direttiva del preprocessore dice al compilatore di includere un file nel programma del codice sorgente. Ci sono due tipi di file che possono essere inclusi dall’utente nel programma:
- File di intestazione o file standard: Questi file contengono la definizione di funzioni predefinite come printf(), scanf() ecc. Questi file devono essere inclusi per lavorare con queste funzioni. Diverse funzioni sono dichiarate in diversi file di intestazione. Per esempio le funzioni I/O standard sono nel file ‘iostream’ mentre le funzioni che eseguono operazioni sulle stringhe sono nel file ‘string’.
Sintassi:
- File di intestazione o file standard: Questi file contengono la definizione di funzioni predefinite come printf(), scanf() ecc. Questi file devono essere inclusi per lavorare con queste funzioni. Diverse funzioni sono dichiarate in diversi file di intestazione. Per esempio le funzioni I/O standard sono nel file ‘iostream’ mentre le funzioni che eseguono operazioni sulle stringhe sono nel file ‘string’.
#include< file_name >
dove nome_file è il nome del file da includere. Le parentesi ‘<‘ e ‘>’ dicono al compilatore di cercare il file nella directory standard.
- file definiti dall’utente: Quando un programma diventa molto grande, è buona pratica dividerlo in file più piccoli e includerlo quando necessario. Questi tipi di file sono file definiti dall’utente. Questi file possono essere inclusi come:
#include"filename"
- Compilazione condizionata: Le direttive di compilazione condizionale sono un tipo di direttive che aiutano a compilare una parte specifica del programma o a saltare la compilazione di una parte specifica del programma in base ad alcune condizioni. Questo può essere fatto con l’aiuto di due comandi di preelaborazione ‘ifdef’ e ‘endif’.
Sintassi:
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif
Se la macro con nome ‘macroname’ è definita allora il blocco di istruzioni verrà eseguito normalmente, ma se non è definita, il compilatore semplicemente salterà questo blocco di istruzioni.
- Altre direttive: Oltre alle direttive di cui sopra ci sono altre due direttive che non sono comunemente usate. Queste sono:
- Direttiva #undef: La direttiva #undef è usata per ridefinire una macro esistente. Questa direttiva funziona come:
#undef LIMIT
Utilizzando questa dichiarazione si ridefinisce la macro esistente LIMIT. Dopo questa dichiarazione ogni dichiarazione “#ifdef LIMIT” valuterà false.
- Direttiva #pragma: Questa direttiva è una direttiva di scopo speciale ed è usata per attivare o disattivare alcune caratteristiche. Questo tipo di direttive sono specifiche del compilatore, cioè variano da compilatore a compilatore. Alcune delle direttive #pragma sono discusse di seguito:
- #pragma startup e #pragma exit: Queste direttive ci aiutano a specificare le funzioni che devono essere eseguite prima dell’avvio del programma (prima che il controllo passi a main()) e appena prima dell’uscita del programma (appena prima che il controllo ritorni da main()).
Nota: Il programma qui sotto non funzionerà con i compilatori GCC.
Guarda il programma qui sotto:
- #pragma startup e #pragma exit: Queste direttive ci aiutano a specificare le funzioni che devono essere eseguite prima dell’avvio del programma (prima che il controllo passi a main()) e appena prima dell’uscita del programma (appena prima che il controllo ritorni da 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()
Il codice di cui sopra produrrà l’output come indicato di seguito quando viene eseguito su compilatori GCC:
Inside main()
Questo accade perché GCC non supporta #pragma startup o exit. Tuttavia, è possibile utilizzare il codice seguente per un output simile sui compilatori 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: Questa direttiva è usata per nascondere i messaggi di avvertimento che vengono visualizzati durante la compilazione.
Possiamo nascondere gli avvertimenti come mostrato di seguito:- #pragma warn -rvl: Questa direttiva nasconde gli avvertimenti che vengono sollevati quando una funzione che dovrebbe restituire un valore non lo restituisce.
- #pragma warn -par: Questa direttiva nasconde gli avvisi che vengono sollevati quando una funzione non utilizza i parametri che le sono stati passati.
- #pragma warn -rch: Questa direttiva nasconde gli avvertimenti che vengono sollevati quando un codice non è raggiungibile. Per esempio: qualsiasi codice scritto dopo la dichiarazione di ritorno in una funzione è irraggiungibile.
Questo articolo è stato contribuito da Harsh Agarwal. Se ti piace GeeksforGeeks e vuoi contribuire, puoi anche scrivere un articolo usando contribute.geeksforgeeks.org o inviare il tuo articolo a [email protected]. Vedi il tuo articolo apparire sulla pagina principale di GeeksforGeeks e aiuta altri Geeks.