C11 und C ++ 11 unterstützen sowohl erweiterte Zeichen in Quelldateien als auch universelle Zeichennamen (Universal Character Names, UCNs), die es ermöglichen, Zeichen einzugeben, die nicht im Basis-Quellzeichensatz enthalten sind und nur Zeichen verwenden, die
sindC ++ 11 definiert auch mehrere Übersetzungsphasen der Kompilierung. Insbesondere werden erweiterte Zeichen in der allerersten Übersetzungsphase, die im Folgenden beschrieben wird, auf UCNs normiert:
§ C ++ 11 2.2p1.1:
Physische Quelldateizeichen werden in einem implementierungsdefinierter Art und Weise, zum grundlegenden Quellzeichensatz (Einführung neuer Zeilenzeichen für Zeilenende-Indikatoren) if notwendig. Die Menge der akzeptierten physischen Quelldateizeichen ist Implementierung definiert. Die Trigrafsequenzen (2.4) werden ersetzt durch entsprechende interne Ein-Zeichen-Darstellungen. Jede Quelle Dateizeichen nicht im Basisquellzeichensatz (2.3) wird ersetzt durch den universellen Charakternamen, der dieses Zeichen bezeichnet. (Ein Implementation kann jede interne Kodierung verwenden, solange sie aktuell ist erweitertes Zeichen in der Quelldatei gefunden, und das gleiche erweitertes Zeichen, das in der Quelldatei als a ausgedrückt wird Universal-Charaktername (d. h. unter Verwendung der \ uXXXX-Notation) äquivalent behandelt, außer wenn diese Ersetzung in a zurückgenommen wird Raw-String-Literal.)
Meine Frage lautet daher:
Ergibt eine standardkonforme Kompilierung des Programms
%Vor%fehlgeschlagen, kompilieren und drucken
%Vor%oder kompilieren und drucken
%Vor%, wenn ausgeführt?
Es ist meine Behauptung, dass die Antwort darin besteht, dass es \u00e9
erfolgreich kompiliert und druckt, denn nach §2.2pl1.1 oben haben wir
Eine Implementierung kann eine interne Codierung verwenden, so lange ein tatsächliches erweitertes Zeichen in der Quelldatei gefunden wird , und dasselbe erweiterte Zeichen, das in der Quelldatei als universal angegeben ist -character-name (dh mit der \ uXXXX-Schreibweise ) wird äquivalent behandelt, außer wenn diese Ersetzung in einem Raw-String-Literal zurückgesetzt wird. , und wir sind nicht in einem rohen String-Literal.
Es folgt dann das
printf("\é\n");
auf printf("\u00e9\n");
. "\u00e9\n"
eins ist. \
auf \
abgebildet, und das Fragment u00e9
wird nicht als UCN erkannt und druckt daher wie es ist. Bedauerlicherweise stimmen die existierenden Compiler mit mir nicht überein. Ich habe sowohl mit GCC 4.8.2 als auch mit Clang 3.5 getestet, und hier haben sie mir Folgendes gegeben:
GCC 4.8.2
%Vor%Clang 3.5
%Vor% Ich habe double- and-triple überprüft, dass das Zeichen é
in Übereinstimmung mit der erwarteten UTF-8-Codierung als C3 A9
unter Verwendung von hexdump -C ucn.cpp
angezeigt wird. Ich habe darüber hinaus überprüft, dass eine einfache printf("é\n");
oder printf("\u00e9\n");
einwandfrei funktioniert, so ist dies kein Problem der getesteten Compiler, UTF-8-Quelldateien nicht lesen zu können.
Wer hat Recht?
'é' ist kein gültiges Zeichen für Backslash-Escape in einem String-Literal. Daher sollte ein umgekehrter Schrägstrich gefolgt von 'é' als literales Quellzeichen oder UCN ein Compiler-Diagnose- und undefiniertes Verhalten erzeugen.
Beachten Sie jedoch, dass "\u00e9"
kein UCN ist, dem ein umgekehrter Schrägstrich vorausgeht, und dass es nicht möglich ist, eine Folge von grundlegenden Quellzeichen in eine Zeichenfolge oder ein Zeichenliteral zu schreiben, das ein umgekehrter Schrägstrich gefolgt von einem UCN ist. Daher müssen sich "\é"
und "\u00e9"
nicht gleich verhalten: Das Verhalten von "\u00e9"
kann perfekt definiert werden, während das Verhalten von "\é"
nicht definiert ist.
Wenn wir eine Syntax setzen würden, die es einem Backslash erlaubt, einem UCN zu entkommen, sagen wir "\«\u00e9»"
, dann hätte das undefiniertes Verhalten wie "\é"
.
- In Phase 1 wird
abgebildetprintf("\é\n");
aufprintf("\u00e9\n");
.
Die Konvertierung von é
in ein UCN in Phase 1 kann kein Nicht-UCN wie "\u00e9"
erstellen.
Die Compiler haben recht, aber behandeln diese Situation nicht mit perfekten Diagnosemeldungen. Im Idealfall erhalten Sie:
%Vor% Beide Compiler geben an, dass ihr Verhalten bei einer unbekannten Escape-Sequenz die Escapesequenz durch das so gematchte Zeichen ersetzt, sodass "\é"
wie "é"
behandelt würde und das Programm insgesamt wie folgt interpretiert werden sollte:
Beide Compiler bekommen dieses Verhalten teilweise zufällig, aber auch teilweise, weil die Richtlinie, unerkannte Escape-Sequenzen so zu behandeln, wie sie es tun, eine kluge Wahl ist: Obwohl sie nur die unerkannte Escape-Sequenz als umgekehrten Schrägstrich sehen Beim Byte 0xC3 entfernen sie den umgekehrten Schrägstrich und lassen die 0xC3 an ihrem Platz, was bedeutet, dass die UTF-8-Sequenz für die spätere Verarbeitung intakt bleibt.
Sie scheinen verwirrt zu sein und denken, dass \u00e9
eine UCN ist - das ist es nicht. UCNs beginnen alle mit \u
, und in Ihrem Fall haben Sie einen Backslash zum Extrahieren, der diesen anfänglichen Backslash vermeidet. Also \u00e9
ist die Folge von 6 Zeichen: \
, u
, 0
, 0
, e
, 9
.
Bearbeiten
In Phase 1 printf ("\ é \ n"); maps to printf ("\ u00e9 \ n");.
Das ist der Punkt, an dem Sie falsch liegen - Phase 1 übersetzt Eingabezeichen in Quellzeichen, also printf("\é\n");
entspricht p
r
i
n
t
f
(
"
\
é
\
n
"
, das ist das gleiche wie )
;
p
r
i
n
t
f
(
"
\
\u00e9
\
n
"
, aber das ist nicht das selbe wie was )
zuordnet, wegen des doppelten Backslash in letzterem. Aufgrund der speziellen Behandlung von Doppel-Backslash gibt es keine Möglichkeit, einen Backslash gefolgt von einem UCN in der Quelle zu haben.
Tags und Links c c++ c++11 language-lawyer c11