Überblick
Der GNU C-Präprozessor (cpp) markiert die allererste Verarbeitungsstufe beim Kompilieren von C- und C++-Quelltexten. In dieser praxisorientierten Anleitung analysieren wir die Funktionsweise des Tools unter Ubuntu direkt auf der Kommandozeile. Sie erfahren, wie sich Makroexpansionen nachvollziehen lassen, wie bedingte Kompilierungszweige zur Fehlersuche gesteuert werden und wie benutzerdefinierte Header-Pfade sauber in den Präprozessordurchlauf integriert werden.
Bereitstellung und Versionsprüfung des cpp-Tools
Um eine vollständige Build-Umgebung einzurichten, wird das Basispaket von GCC über den Paketmanager installiert: apt-get update -qq && apt-get install -y gcc.

Mit which cpp lässt sich anschließend verifizieren, an welcher Stelle im Dateisystem das Binary des Präprozessors hinterlegt ist.
Die Rückmeldung /usr/bin/cpp belegt die einwandfreie Einbindung in die Standardpfade des Betriebssystems.

Über das Kommando cpp --version rufen wir detaillierte Angaben zur installierten GNU-Präprozessorversion ab.
Die Ausgabe bestätigt die Verfügbarkeit von Release 13.3.0 auf der Ubuntu-Instanz.
⚙️ [Wichtige Optionen]
-D name : Definiert ein Makro direkt über die Befehlszeile, nützlich für Build-Schalter oder Konstanten.-U name : Hebt eine bestehende oder systemweit vorgegebene Makrodefinition auf.-I dir : Ergänzt ein weiteres Verzeichnis zur Suche nach eingebundenen Headerdateien (#include).-dM : Gibt eine vollständige Liste aller während der Ausführung aktiven Makros aus.-dD : Gibt sowohl die #define-Anweisungen als auch den transformierten Quellcode aus.-P : Unterdrückt die Generierung störender Zeilennummernmarkierungen (#line).-v : Zeigt ausführliche Debuginformationen sowie die standardmäßigen Include-Pfade an.

Makroexpansion und Textersetzung in der Praxis
Zur sauberen Kapselung der Testdateien erstellen wir ein separates Verzeichnis und wechseln dorthin mit mkdir -p ~/cpp_practice && cd ~/cpp_practice.

Mit dem Befehl printf '#define BUFFER_SIZE 1024\n#define SQUARE(x) ((x) * (x))\nint limit = BUFFER_SIZE;\nint area = SQUARE(5);\n' > macro_test.c erzeugen wir ein C-Programm, das sowohl eine einfache Konstante als auch ein funktionales Makro enthält.

Der Aufruf cpp macro_test.c output.i veranlasst den Präprozessor, alle Direktiven aufzulösen und das reine Expansionsergebnis in die Zwischendatei output.i zu schreiben.
Makros und Quellcodeverweise werden dabei vollständig in ihre Zielstrukturen überführt.

Mittels cat output.i | grep -v '^#' filtern wir die internen Linemarker-Zeilen heraus, um ausschließlich den transformierten C-Code zu begutachten.
Hier wird deutlich sichtbar, wie BUFFER_SIZE nahtlos durch 1024 ersetzt wurde und der Aufruf SQUARE(5) in die Berechnung ((5) * (5)) expandiert ist.

Bedingte Übersetzung steuern mit -D und -U
Wir navigieren mit cd ~/cpp_practice zurück in das Projektverzeichnis, um die nächsten Tests vorzubereiten.

Eine weitere Datei für bedingte Verzweigungen entsteht über printf '#ifdef DEBUG\nconst char *status = "DEBUG_ENABLED";\n#else\nconst char *status = "PRODUCTION";\n#endif\n' > condition_test.c.

Durch cpp -DDEBUG -P condition_test.c injizieren wir das Symbol DEBUG direkt über die Kommandozeile und deaktivieren zugleich alle Zeilenmarkierungen.
Infolgedessen evaluiert der #ifdef-Block zu wahr, sodass die Zeile const char *status = "DEBUG_ENABLED"; ausgegeben wird.

Im gegenteiligen Fall widerrufen wir das Makro explizit mittels cpp -UDEBUG -P condition_test.c.
Da das Flag -U eine Definition verhindert, greift der #else-Pfad und liefert wie erwartet const char *status = "PRODUCTION";.

Header-Suchpfade anpassen und Ausgabe bereinigen (-I, -P)
Erneut stellen wir den aktuellen Pfad mit cd ~/cpp_practice sicher.

Für externe Schnittstellendefinitionen legen wir mit mkdir -p custom_headers ein separates Include-Verzeichnis an.

Darin hinterlegen wir Versionsinformationen über den Befehl echo '#define APP_VERSION "1.0.0"' > custom_headers/version.h.

Nun binden wir diese Datei in ein neues Testprogramm ein: printf '#include "version.h"\nconst char *ver = APP_VERSION;\n' > main_app.c.

Mit cpp -I./custom_headers -P main_app.c weisen wir den Präprozessor an, das zusätzliche Verzeichnis zu scannen und störende Markierungen wegzulassen.
Dank -I löst das Werkzeug die Datei version.h reibungslos auf, während der Parameter -P für eine minimalistische Ausgabe sorgt, die unmittelbar const char *ver = "1.0.0"; zeigt.

Damit beherrschen Sie die grundlegenden Einsatzszenarien des C-Präprozessors unter Ubuntu. Die isolierte Betrachtung der Präprozessorstufe erweist sich insbesondere bei verschachtelten Makrokonstrukten und kryptischen Compilerfehlern als unverzichtbares Diagnosewerkzeug.