Wie der Name schon sagt, sind Präprozessoren Programme, die unseren Quellcode vor der Kompilierung bearbeiten. Zwischen dem Schreiben eines Programms und der Ausführung eines Programms in C / C++ gibt es eine Reihe von Schritten. Schauen wir uns diese Schritte an, bevor wir anfangen, etwas über Präprozessoren zu lernen.
Die Zwischenschritte sind in dem obigen Diagramm zu sehen. Der von Programmierern geschriebene Quellcode wird in der Datei program.c gespeichert. Diese Datei wird dann von Präprozessoren verarbeitet und es wird eine erweiterte Quellcodedatei namens program erzeugt. Diese erweiterte Datei wird vom Compiler kompiliert und es wird eine Objektcodedatei mit dem Namen program.obj erzeugt. Schließlich verknüpft der Linker diese Objektcodedatei mit dem Objektcode der Bibliotheksfunktionen, um die ausführbare Datei program.exe zu erzeugen.
Präprozessorprogramme stellen Präprozessoranweisungen bereit, die den Compiler anweisen, den Quellcode vor dem Kompilieren vorzuverarbeiten. Alle diese Präprozessoranweisungen beginnen mit einem ‚#‘-Symbol (Raute). Das ‚#‘-Symbol zeigt an, dass jede Anweisung, die mit # beginnt, an das Präprozessorprogramm weitergeleitet wird, und das Präprozessorprogramm wird diese Anweisung ausführen. Beispiele für einige Präprozessor-Direktiven sind: #include, #define, #ifndef usw. Denken Sie daran, dass das #-Symbol nur einen Pfad zum Präprozessor angibt, und dass Befehle wie include vom Präprozessorprogramm verarbeitet werden. Zum Beispiel wird include zusätzlichen Code in Ihr Programm einfügen. Wir können diese Präprozessor-Direktiven überall in unserem Programm platzieren.
Es gibt 4 Haupttypen von Präprozessoranweisungen:
- Makros
- Datei-Einbindung
- Bedingte Kompilierung
- Andere Direktiven
Lernen wir nun jede dieser Direktiven im Detail kennen.
- Makros: Makros sind ein Teil des Codes in einem Programm, der einen Namen erhält. Wann immer der Compiler auf diesen Namen stößt, ersetzt er ihn durch das eigentliche Codestück. Die ‚#define‘-Direktive wird verwendet, um ein Makro zu definieren. Lassen Sie uns nun die Makrodefinition anhand eines Programms verstehen:
#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;
}
Ausgabe:
01234
Im obigen Programm ersetzt der Compiler bei der Ausführung das Wort LIMIT durch 5. Das Wort ‚LIMIT‘ in der Makrodefinition wird als Makroschablone bezeichnet und ‚5‘ ist eine Makroerweiterung.
Hinweis: Am Ende der Makrodefinition steht kein Semikolon(‚;‘). Makrodefinitionen müssen nicht mit einem Semikolon abgeschlossen werden.
Makros mit Argumenten: Wir können auch Argumente an Makros übergeben. Makros, die mit Argumenten definiert werden, funktionieren ähnlich wie Funktionen. Lassen Sie uns dies anhand eines Programms verstehen:
#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;
}
Ausgabe:
Area of rectangle is: 50
Wir können aus dem obigen Programm erkennen, dass der Compiler jedes Mal, wenn er AREA(l, b) im Programm findet, es durch die Anweisung (l*b) ersetzt. Darüber hinaus werden auch die Werte, die an die Makroschablone AREA(l, b) übergeben werden, durch die Anweisung (l*b) ersetzt. Daher wird AREA(10, 5) gleich 10*5 sein.
- Datei-Inklusion: Diese Art von Präprozessoranweisung weist den Compiler an, eine Datei in das Quelltextprogramm einzuschließen. Es gibt zwei Arten von Dateien, die vom Benutzer in das Programm aufgenommen werden können:
- Header-Datei oder Standard-Dateien: Diese Dateien enthalten die Definition von vordefinierten Funktionen wie printf(), scanf() usw. Diese Dateien müssen enthalten sein, um mit diesen Funktionen arbeiten zu können. Verschiedene Funktionen werden in verschiedenen Header-Dateien deklariert. Zum Beispiel befinden sich Standard-E/A-Funktionen in der Datei ‚iostream‘, während Funktionen, die String-Operationen durchführen, in der Datei ’string‘ enthalten sind.
Syntax:
- Header-Datei oder Standard-Dateien: Diese Dateien enthalten die Definition von vordefinierten Funktionen wie printf(), scanf() usw. Diese Dateien müssen enthalten sein, um mit diesen Funktionen arbeiten zu können. Verschiedene Funktionen werden in verschiedenen Header-Dateien deklariert. Zum Beispiel befinden sich Standard-E/A-Funktionen in der Datei ‚iostream‘, während Funktionen, die String-Operationen durchführen, in der Datei ’string‘ enthalten sind.
#include< file_name >
wobei ‚Dateiname‘ der Name der einzubindenden Datei ist. Die Klammern ‚<‚ und ‚>‘ weisen den Compiler an, die Datei im Standardverzeichnis zu suchen.
- Benutzerdefinierte Dateien: Wenn ein Programm sehr groß wird, ist es sinnvoll, es in kleinere Dateien aufzuteilen und bei Bedarf einzubinden. Diese Art von Dateien sind benutzerdefinierte Dateien. Diese Dateien können wie folgt eingebunden werden:
#include"filename"
- Bedingte Kompilierung: Bedingte Kompilierungsanweisungen sind eine Art von Anweisungen, die dabei helfen, einen bestimmten Teil des Programms zu kompilieren oder die Kompilierung eines bestimmten Teils des Programms auf der Grundlage einiger Bedingungen zu überspringen. Dies kann mit Hilfe der beiden Vorverarbeitungsbefehle ‚ifdef‘ und ‚endif‘ geschehen.
Syntax:
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif
Wenn das Makro mit dem Namen ‚macroname‘ definiert ist, wird der Anweisungsblock normal ausgeführt, aber wenn es nicht definiert ist, überspringt der Compiler diesen Anweisungsblock einfach.
- Andere Direktiven: Neben den oben genannten Direktiven gibt es zwei weitere Direktiven, die nicht häufig verwendet werden. Diese sind:
- #undef-Direktive: Die #undef-Direktive wird verwendet, um ein bestehendes Makro zu entdefinieren. Diese Direktive funktioniert wie folgt:
#undef LIMIT
Mit dieser Anweisung wird das vorhandene Makro LIMIT undefiniert. Nach dieser Anweisung wird jede „#ifdef LIMIT“-Anweisung als falsch ausgewertet.
- #pragma-Richtlinie: Diese Direktive ist eine Spezialdirektive und wird verwendet, um einige Funktionen ein- oder auszuschalten. Diese Art von Direktiven sind compilerspezifisch, d.h. sie variieren von Compiler zu Compiler. Einige der #pragma-Direktiven werden im Folgenden erläutert:
- #pragma startup und #pragma exit: Diese Direktiven helfen uns, die Funktionen zu spezifizieren, die vor dem Programmstart (bevor die Kontrolle an main() übergeben wird) und kurz vor dem Programmende (kurz bevor die Kontrolle von main() zurückkehrt) ausgeführt werden müssen.
Hinweis: Das untenstehende Programm funktioniert nicht mit GCC-Compilern.
Betrachten Sie das folgende Programm:
- #pragma startup und #pragma exit: Diese Direktiven helfen uns, die Funktionen zu spezifizieren, die vor dem Programmstart (bevor die Kontrolle an main() übergeben wird) und kurz vor dem Programmende (kurz bevor die Kontrolle von main() zurückkehrt) ausgeführt werden müssen.
#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;
}
Ausgabe:
Inside func1()Inside main()Inside func2()
Der obige Code erzeugt die unten angegebene Ausgabe, wenn er auf GCC-Compilern ausgeführt wird:
Inside main()
Dies geschieht, weil GCC #pragma startup oder exit nicht unterstützt. Sie können jedoch den folgenden Code verwenden, um eine ähnliche Ausgabe auf GCC-Compilern zu erhalten.
#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 Richtlinie: Diese Direktive wird verwendet, um die Warnmeldungen, die während der Kompilierung angezeigt werden, auszublenden.
Wir können die Warnungen wie unten gezeigt ausblenden:- #pragma warn -rvl: Diese Direktive verbirgt die Warnungen, die ausgegeben werden, wenn eine Funktion, die einen Wert zurückgeben soll, keinen Wert zurückgibt.
- #pragma warn -par: Diese Direktive verbirgt die Warnungen, die ausgegeben werden, wenn eine Funktion die ihr übergebenen Parameter nicht verwendet.
- #pragma warn -rch: Diese Direktive verbirgt die Warnungen, die ausgelöst werden, wenn ein Code unerreichbar ist. Zum Beispiel: Jeder Code, der nach der Return-Anweisung in einer Funktion geschrieben wird, ist unerreichbar.
Dieser Artikel wurde von Harsh Agarwal verfasst. Wenn Ihnen GeeksforGeeks gefällt und Sie einen Beitrag leisten möchten, können Sie auch einen Artikel unter contribute.geeksforgeeks.org schreiben oder eine E-Mail an [email protected] senden. Sehen Sie, wie Ihr Artikel auf der GeeksforGeeks-Hauptseite erscheint und helfen Sie anderen Geeks.