Das Geheimnis von #include in C++: <file> vs "file" — Geschwindigkeit und Optimierung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Ein kleines Detail, das Sie Sekunden der Kompilierung kosten kann

\n\n

Stellen Sie sich vor: Sie schreiben ein C++-Projekt. Die Arbeit läuft, der Code wächst, die Ordner mit Dateien vermehren sich. Und dann tippen Sie wieder #include. Wie gewohnt – in spitzen Klammern? Oder in Anführungszeichen? Und welchen Unterschied macht das eigentlich? Der Compiler ist doch schlau und findet es selbst.

\n\n

Spoiler: Er wird es finden. Aber nicht immer schnell. Und nicht immer die Datei, die Sie gemeint haben. Die Frage nach dem Unterschied zwischen #include <filename> und #include "filename" ist eine der beliebtesten auf Stack Overflow (Hunderttausende von Aufrufen). Und es geht nicht nur um syntaktischen Zucker. Es geht um Kompilierungsleistung, Kontrolle über Abhängigkeiten und letztendlich um die Qualität Ihres Codes.

\n\n

In diesem Artikel werden wir nicht nur die Regeln auflisten. Wir werden unter die Haube des Präprozessors schauen, reale Beispiele analysieren und praktische Tipps geben, wie Sie den Build Ihres Projekts beschleunigen können. Los geht's!

\n\n

Wie funktioniert der Präprozessor? Anatomie der #include-Direktive

\n\n

Bevor der Compiler Ihren Code sieht, kommt der Präprozessor zum Einsatz. Seine Aufgabe ist es, die "Quelldatei" für die Kompilierung vorzubereiten. Die Direktive #include ist ein Befehl: "Nimm den Inhalt der Datei X und füge ihn genau hier ein, anstelle dieser Zeile."

\n\n

Klingt einfach. Aber der Teufel steckt, wie immer, im Detail. Und zwar darin, wo der Präprozessor nach dieser Datei X sucht.

\n\n

Spitze Klammern: #include <filename>

\n

Wenn Sie #include <iostream> schreiben, begibt sich der Präprozessor ausschließlich auf die Suche entlang der standardmäßigen Systempfade. Unter Linux sind das normalerweise /usr/include, /usr/local/include und Pfade, die dem Compiler über das Flag -I (oder /I in Visual Studio) übergeben wurden.

\n

Entscheidender Punkt: Der Präprozessor schaut NICHT in das aktuelle Projektverzeichnis. Nur in die Ordner, die ihm als "System-" oder "Include-Suchpfade" angegeben wurden.

\n

Dies wurde absichtlich so gemacht: um zu verhindern, dass versehentlich eine lokale Datei mit demselben Namen anstelle der Standardbibliothek eingebunden wird.

\n\n

Doppelte Anführungszeichen: #include "filename"

\n

Bei Anführungszeichen ist die Geschichte komplexer und interessanter. Der C++-Standard (Abschnitt 16.2) sagt: Das Verhalten ist implementierungsabhängig. Aber in der Praxis arbeiten 99% der Compiler (GCC, Clang, MSVC) nach dem gleichen Prinzip:

\n
    \n
  1. Zuerst Suche in dem Verzeichnis, in dem sich die includierende Datei selbst befindet (normalerweise der aktuelle Ordner mit der Quelldatei).
  2. \n
  3. Dann Suche entlang der gleichen Pfade wie für spitze Klammern (System + -I).
  4. \n
\n

Das heißt, #include "myheader.h" überprüft zuerst den Ordner mit Ihrer main.cpp. Gefunden – prima. Nicht gefunden – geht es zur Systemliste.

\n\n

Ist das nur Wissen oder echte Optimierung?

\n

"Na und?", werden Sie fragen. "Computer sind schnell, welchen Unterschied macht es, wo gesucht wird?"

\n

Es gibt einen Unterschied, und er setzt sich aus zwei Faktoren zusammen: Suchgeschwindigkeit und Klarheit der Absicht.

\n\n

Faktor 1: Geschwindigkeit der Dateisuche

\n

In einem kleinen Projekt (10-20 Dateien) werden Sie keinen Unterschied bemerken. Aber in einem echten Enterprise-Produkt oder einer Spiel-Engine kann die Anzahl der Includes in die Zehntausende gehen. Jedes Mal, wenn Sie #include "some_system_lib.h" schreiben, verschwendet der Präprozessor zusätzliche Zeit mit der Überprüfung des aktuellen Ordners und aller Unterordner (abhängig von der Implementierung).

\n

Ein Beispiel aus der Praxis: Stellen Sie sich vor, Sie haben ein Projekt mit 10.000 Dateien. 20% davon verwenden Anführungszeichen für Standardbibliotheken. Jedes solche Include führt einen zusätzlichen Systemaufruf stat() oder access() im aktuellen Verzeichnis durch. Das sind Mikrosekunden, aber summiert über das gesamte Projekt – echte Sekunden der Kompilierung.

\n\n

Die Verwendung von #include <iostream> anstelle von #include "iostream" stellt sicher, dass die Suche sofort bei den Systempfaden beginnt und das lokale Verzeichnis übersprungen wird. Dies gibt

Blogs

Buchempfehlungen