Übersetzungsphasen
C++-Quellcodedateien werden vom Compiler verarbeitet, um C++-Programme zu erzeugen.
[edit] Übersetzungsprozess
Der Text eines C++-Programms wird in Einheiten namens Quellcodedateien gehalten.
C++-Quellcodedateien durchlaufen eine Übersetzung, um eine Übersetzungseinheit zu werden, bestehend aus den folgenden Schritten:
- Ordnet jede Quelldatei einer Zeichensequenz zu.
- Konvertiert jede Zeichensequenz in eine Sequenz von Präprozessor-Tokens, getrennt durch Leerzeichen.
- Konvertiert jedes Präprozessor-Token in ein Token und bildet eine Token-Sequenz.
- Konvertiert jede Token-Sequenz in eine Übersetzungseinheit.
Ein C++-Programm kann aus übersetzten Übersetzungseinheiten gebildet werden. Übersetzungseinheiten können separat übersetzt und später gelinkt werden, um ein ausführbares Programm zu erzeugen.
Der obige Prozess kann in 9 Übersetzungsphasen organisiert werden.
[edit] Präprozessor-Tokens
Ein Präprozessor-Token ist das minimale lexikalische Element der Sprache in den Übersetzungsphasen 3 bis 6.
Die Kategorien von Präprozessor-Tokens sind:
- Header-Namen (wie <iostream> oder "myfile.h")
|
(seit C++20) |
- Bezeichner
- Präprozessor-Zahlen (siehe unten)
- Zeichenliterale, einschließlich benutzerdefinierter Zeichenliterale(seit C++11)
- String-Literale, einschließlich benutzerdefinierter String-Literale(seit C++11)
- Operatoren und Satzzeichen, einschließlich alternativer Tokens
- einzelne Nicht-Leerzeichen-Zeichen, die in keine andere Kategorie passen
- Das Programm ist fehlerhaft, wenn das Zeichen, das dieser Kategorie entspricht, Folgendes ist:
- Apostroph (', U+0027),
- Anführungszeichen (", U+0022) oder
- ein Zeichen, das nicht im Basiszeichensatz enthalten ist.
[edit] Präprozessor-Zahlen
Die Menge der Präprozessor-Tokens von Präprozessor-Zahlen ist eine Obermenge der Vereinigung der Mengen von Tokens von Ganzzahl-Literalen und Gleitkomma-Literalen
.(optional) Ziffer pp-continue-seq (optional) |
|||||||||
| Ziffer | - | eine der Ziffern 0-9 |
| pp-continue-seq | - | eine Sequenz von pp-continue s |
Jedes pp-continue ist eines der folgenden:
| identifier-continue | (1) | ||||||||
| exp-char sign-char | (2) | ||||||||
.
|
(3) | ||||||||
’ Ziffer |
(4) | (seit C++14) | |||||||
’ nondigit |
(5) | (seit C++14) | |||||||
| identifier-continue | - | jedes Zeichen außer dem ersten eines gültigen Bezeichners |
| exp-char | - | eines von P, p, (seit C++11) E und e |
| sign-char | - | eines von + und - |
| Ziffer | - | eine der Ziffern 0-9 |
| nondigit | - | eines der lateinischen Buchstaben A/a-Z/z und Unterstrich |
Eine Präprozessor-Zahl hat keinen Typ oder Wert; sie erhält beides nach einer erfolgreichen Konvertierung in ein Ganzzahl-/Gleitkomma-Literal-Token.
[edit] Leerzeichen
Leerzeichen besteht aus Kommentaren, Leerzeichen oder beidem.
Die folgenden Zeichen sind Leerzeichen:
- Zeichen-Tabulator (U+0009)
- Zeilenumbruch / Zeilenvorschub-Zeichen (U+000A)
- Zeilentabulator (U+000B)
- Seitenumbruch (U+000C)
- Leerzeichen (U+0020)
Leerzeichen werden normalerweise verwendet, um Präprozessor-Tokens zu trennen, mit den folgenden Ausnahmen:
- Es ist kein Trennzeichen in Header-Namen, Zeichenliteralen und String-Literalen.
- Durch Leerzeichen, die Zeilenumbrüche enthalten, getrennte Präprozessor-Tokens können keine Präprozessor-Anweisungen bilden.
#include "my header" // OK, using a header name containing whitespace #include/*hello*/<iostream> // OK, using a comment as whitespace #include <iostream> // Error: #include cannot span across multiple lines "str ing" // OK, a single preprocessing token (string literal) ' ' // OK, a single preprocessing token (character literal)
[edit] Maximaler Munch
Wenn die Eingabe bis zu einem bestimmten Zeichen in Präprozessor-Tokens zerlegt wurde, wird das nächste Präprozessor-Token im Allgemeinen als die längste Zeichensequenz genommen, die ein Präprozessor-Token bilden könnte, auch wenn dies zu einem späteren Analysefehler führen würde. Dies ist allgemein bekannt als maximaler Munch.
int foo = 1; int bar = 0xE+foo; // Error: invalid preprocessing number 0xE+foo int baz = 0xE + foo; // OK
Mit anderen Worten, die Regel des maximalen Munch begünstigt mehrere Zeichen umfassende Operatoren und Satzzeichen.
int foo = 1; int bar = 2; int num1 = foo+++++bar; // Error: treated as “foo++ ++ +baz”, not “foo++ + ++baz” int num2 = -----foo; // Error: treated as “-- -- -foo”, not “- -- --foo”
Die Regel des maximalen Munch hat die folgenden Ausnahmen:
- Header-Namen-Präprozessor-Tokens werden nur in folgenden Fällen gebildet:
- nach dem Präprozessor-Token include in einer #include-Anweisung
|
(seit C++17) |
|
(seit C++20) |
std::vector<int> x; // OK, “int” is not a header name
- Wenn die nächsten drei Zeichen <:: sind und das nachfolgende Zeichen weder : noch > ist, wird das < als einzelnes Präprozessor-Token behandelt und nicht als erstes Zeichen des alternativen Tokens <:.
struct Foo { static const int v = 1; }; std::vector<::Foo> x; // OK, <: not taken as the alternative token for [ extern int y<::>; // OK, same as “extern int y[];” int z<:::Foo::value:>; // OK, same as “int z[::Foo::value];”
template<int i> class X { /* ... */ }; template<class T> class Y { /* ... */ }; Y<X<1>> x3; // OK, declares a variable “x3” of type “Y<X<1> >” Y<X<6>>1>> x4; // Syntax error Y<X<(6>>1)>> x5; // OK
#define R "x" const char* s = R"y"; // ill-formed raw string literal, not "x" "y" const char* s2 = R"(a)" "b)"; // a raw string literal followed by a normal string literal |
(seit C++11) |
[edit] Tokens
Ein Token ist das minimale lexikalische Element der Sprache in Übersetzungsphase 7.
Die Kategorien von Tokens sind:
- Bezeichner
- Schlüsselwörter
- Literale
- Operatoren und Satzzeichen (außer Präprozessor-Operatoren)
[edit] Übersetzungsphasen
Die Übersetzung wird as if in der Reihenfolge von Phase 1 bis Phase 9 durchgeführt. Implementierungen verhalten sich so, als ob diese separaten Phasen stattfinden, obwohl in der Praxis verschiedene Phasen zusammengelegt werden können.
[edit] Phase 1: Abbildung von Quellzeichen
|
1) Die einzelnen Bytes der Quellcodedatei werden (auf implementierungsabhängige Weise) auf die Zeichen des Basiszeichensatzes der Quelle abgebildet. Insbesondere werden betriebssystemabhängige Zeilenende-Indikatoren durch neue Zeilenzeichen ersetzt.
2) Der Satz der akzeptierten Quellcodedateizeichen ist implementierungsabhängig(seit C++11). Jedes Quellcodedateizeichen, das nicht auf ein Zeichen im Basiszeichensatz der Quelle abgebildet werden kann, wird durch seinen universellen Zeichentitel (escaped mit
\u oder \U) oder durch eine implementierungsabhängige Form ersetzt, die äquivalent behandelt wird.
|
(bis C++23) | ||
|
Eingabedateien, die eine Sequenz von UTF-8-Codeeinheiten (UTF-8-Dateien) sind, werden garantiert unterstützt. Die Menge anderer unterstützter Arten von Eingabedateien ist implementierungsabhängig. Wenn die Menge nicht leer ist, wird die Art einer Eingabedatei auf implementierungsabhängige Weise bestimmt, die eine Möglichkeit zur Bezeichnung von Eingabedateien als UTF-8-Dateien einschließt, unabhängig von ihrem Inhalt (das Erkennen des Byte Order Mark ist nicht ausreichend).
|
(seit C++23) |
[edit] Phase 2: Zusammenfügen von Zeilen
[edit] Phase 3: Lexikalische Analyse
// The following #include directive can de decomposed into 5 preprocessing tokens: // punctuators (#, < and >) // │ // ┌────────┼────────┐ // │ │ │ #include <iostream> // │ │ // │ └── header name (iostream) // │ // └─────────── identifier (include)
// Error: partial string literal "abc
// Error: partial comment /* comment
|
Während Zeichen aus der Quelldatei verbraucht werden, um das nächste Präprozessor-Token zu bilden (d. h. nicht als Teil eines Kommentars oder anderer Formen von Leerzeichen verbraucht werden), werden universelle Zeichentitel erkannt und durch das bezeichnete Element des Übersetzungszeichensatzes ersetzt, außer wenn eine Zeichensequenz in einem der folgenden Präprozessor-Tokens abgeglichen wird:
|
(seit C++23) |
|
2) Alle Transformationen, die während Phase 1 und(bis C++23) Phase 2 zwischen dem anfänglichen und dem abschließenden Anführungszeichen eines beliebigen Raw-String-Literals durchgeführt wurden, werden rückgängig gemacht.
|
(seit C++11) |
- Jeder Kommentar wird durch ein Leerzeichen ersetzt.
- Zeilenumbruchzeichen bleiben erhalten.
- Ob jede nicht leere Sequenz von Leerzeichen außer Zeilenumbruch erhalten bleibt oder durch ein Leerzeichen ersetzt wird, ist nicht spezifiziert.
[edit] Phase 4: Präprozessor
[edit] Phase 5: Bestimmung gemeinsamer String-Literal-Kodierungen
|
1) Alle Zeichen in Zeichenliteralen und String-Literalen werden vom Quellzeichensatz in die Kodierung konvertiert (was eine Multibyte-Zeichenkodierung wie UTF-8 sein kann, solange die 96 Zeichen des Basiszeichensatzes Ein-Byte-Darstellungen haben).
2) Escape-Sequenzen und universelle Zeichentitel in Zeichenliteralen und Nicht-Raw-String-Literalen werden erweitert und in die Literal-Kodierung konvertiert. Wenn das Zeichen, das durch einen universellen Zeichentitel spezifiziert wird, nicht als einzelner Code-Punkt in der entsprechenden Literal-Kodierung kodiert werden kann, ist das Ergebnis implementierungsabhängig, aber es wird garantiert, dass es kein Null- (breites) Zeichen ist. |
(bis C++23) |
|
Für eine Sequenz von zwei oder mehr benachbarten String-Literal-Tokens wird ein gemeinsames Kodierungspräfix bestimmt, wie hier beschrieben. Jedes solche String-Literal-Token gilt dann als dieses gemeinsame Kodierungspräfix habend. (Zeichenkonvertierung wird in Phase 3 verschoben) |
(seit C++23) |
[edit] Phase 6: Verketten von String-Literalen
Benachbarte String-Literale werden verkettet.
[edit] Phase 7: Kompilieren
Die Kompilierung findet statt: jedes Präprozessor-Token wird in ein Token konvertiert. Die Tokens werden syntaktisch und semantisch analysiert und als Übersetzungseinheit übersetzt.
[edit] Phase 8: Instanziieren von Templates
Jede Übersetzungseinheit wird untersucht, um eine Liste der erforderlichen Template-Instanziierungen zu erzeugen, einschließlich derjenigen, die durch explizite Instanziierungen angefordert werden. Die Definitionen der Templates werden gefunden, und die erforderlichen Instanziierungen werden durchgeführt, um Instanziierungseinheiten zu erzeugen.
[edit] Phase 9: Linken
Übersetzungseinheiten, Instanziierungseinheiten und Bibliothekskomponenten, die zur Erfüllung externer Referenzen benötigt werden, werden zu einem Programm-Image gesammelt, das Informationen enthält, die für die Ausführung in seiner Ausführungsumgebung benötigt werden.
[edit] Anmerkungen
Quellcodedateien, Übersetzungseinheiten und übersetzte Übersetzungseinheiten müssen nicht unbedingt als Dateien gespeichert werden, noch muss es eine Eins-zu-Eins-Korrespondenz zwischen diesen Entitäten und einer externen Darstellung geben. Die Beschreibung ist rein konzeptionell und gibt keine bestimmte Implementierung an.
|
Die in Phase 5 durchgeführte Konvertierung kann in einigen Implementierungen durch Kommandozeilenoptionen gesteuert werden: gcc und clang verwenden -finput-charset, um die Kodierung des Quellzeichensatzes anzugeben, -fexec-charset und -fwide-exec-charset, um die normalen und breiten Literal-Kodierungen anzugeben, während Visual Studio 2015 Update 2 und neuere Versionen /source-charset und /execution-charset verwenden, um den Quellzeichensatz und die Literal-Kodierung anzugeben. |
(bis C++23) |
Einige Compiler implementieren keine Instanziierungseinheiten (auch bekannt als Template-Repositories oder Template-Registries) und kompilieren jede Template-Instanziierung einfach in Phase 7, speichern den Code in der Objektdatei, wo er implizit oder explizit angefordert wird, und der Linker kollabiert dann diese kompilierten Instanziierungen in Phase 9 zu einer.
[edit] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 787 | C++98 | das Verhalten war undefiniert, wenn eine nicht leere Quelldatei am Ende von Phase 2 keinen Zeilenumbruch hatte |
ein abschließendes Newline-Zeichen hinzufügen Zeichen in diesem Fall |
| CWG 1104 | C++98 | das alternative Token <: führte dazu, dass std::vector<::std::string> als std::vector[:std::string> behandelt wurde |
eine zusätzliche lexikalische Regel hinzugefügt, um diesen Fall zu behandeln |
| CWG 1775 | C++11 | das Bilden eines universellen Zeichentitels innerhalb eines Raw- String-Literals in Phase 2 führte zu undefiniertem Verhalten |
wurde wohlformuliert |
| CWG 2747 | C++98 | Phase 2 überprüfte den End-of-File-Splice nach dem Spleißen, dies ist unnötig | die Überprüfung wurde entfernt |
| P2621R3 | C++98 | universelle Zeichentitel durften nicht durch Zeilen-Spleißen oder Token-Verkettung gebildet werden |
erlaubt |
[edit] Referenzen
- C++23 Standard (ISO/IEC 14882:2024)
- 5.2 Phasen der Übersetzung [lex.phases]
- C++20 Standard (ISO/IEC 14882:2020)
- 5.2 Phasen der Übersetzung [lex.phases]
- C++17 Standard (ISO/IEC 14882:2017)
- 5.2 Phasen der Übersetzung [lex.phases]
- C++14 Standard (ISO/IEC 14882:2014)
- 2.2 Phasen der Übersetzung [lex.phases]
- C++11 Standard (ISO/IEC 14882:2011)
- 2.2 Phasen der Übersetzung [lex.phases]
- C++03-Standard (ISO/IEC 14882:2003)
- 2.1 Phasen der Übersetzung [lex.phases]
- C++98 Standard (ISO/IEC 14882:1998)
- 2.1 Phasen der Übersetzung [lex.phases]
[edit] Siehe auch
| C-Dokumentation für Phasen der Übersetzung
|