C11 и C ++ 11 поддерживают расширенные символы в исходных файлах, а также универсальные имена символов (UCN), которые позволяют вводить символы, отсутствующие в базовом наборе исходных символов, используя только символы.
C ++ 11 также определяет несколько этапов трансляции компиляции. В частности, расширенные символы нормализуются к UCN на самом первом этапе перевода, описанном ниже:
§ C ++ 11 2.2p1.1:
Физические символы исходного файла отображаются в
определенным реализацией способом, к основному исходному набору символов
(введение символов новой строки для индикаторов конца строки), если
необходимо. Допустимый набор физических символов исходного файла:
реализации. Триграфные последовательности (2.4) заменены на
соответствующие односимвольные внутренние представления. Любой источник
символ файла, не входящий в базовый исходный набор символов (2.3), заменяется
универсальным именем персонажа, которое обозначает этот символ. (An
реализация может использовать любую внутреннюю кодировку, пока фактическая
расширенный символ, встречающийся в исходном файле, и тот же
расширенный символ, выраженный в исходном файле как
имя универсального символа (т. е. с использованием нотации \ uXXXX)
обрабатывается аналогично, за исключением случаев, когда эта замена возвращается в
необработанный строковый литерал.)
Поэтому мой вопрос:
Соответствует ли стандартная компиляция программы?
#include <stdio.h> int main(void){ printf("\é\n"); printf("\\u00e9\n"); return 0; }
потерпеть неудачу, скомпилировать и распечатать
é é
или скомпилировать и распечатать
\u00e9 \u00e9
, когда бегать?
Я утверждаю, что ответ заключается в том, что он успешно компилирует и печатает \u00e9
поскольку согласно §2.2p1.1 выше мы имеем
Реализация может использовать любую внутреннюю кодировку, до тех пор, пока фактический расширенный символ встречается в исходном файле, а также тот же расширенный символ, который указан в исходном файле как универсальное имя (Т.е. используя обозначение \ uXXXX), обрабатываются эквивалентно за исключением случаев, когда эта замена возвращается в виде необработанного строкового литерала., и мы не в необработанном строковом литерале.
Из этого следует, что
printf("\é\n");
карты для printf("\\u00e9\n");
,"\\u00e9\n"
это один.\\
карты для \
и фрагмент u00e9
не распознается как UCN и поэтому печатается как есть.К сожалению, существующие компиляторы не согласны со мной. Я тестировал как GCC 4.8.2, так и Clang 3.5, и вот что они мне дали:
GCC 4.8.2
$ g++ -std=c++11 -Wall -Wextra ucn.cpp -o ucn
ucn.cpp: In function 'int main()':
ucn.cpp:4:9: warning: unknown escape sequence: '\303' [enabled by default]
printf("\é\n");
^
$ ./ucn
é
\u00e9
Clang 3.5
$ clang++ -std=c++11 -Wall -Wextra ucn.cpp -o ucn
ucn.cpp:4:10: warning: unknown escape sequence '\xFFFFFFC3' [-Wunknown-escape-sequence]
printf("\é\n");
^
ucn.cpp:4:12: warning: illegal character encoding in string literal [-Winvalid-source-encoding]
printf("\é\n");
^
2 warnings generated.
$ ./ucn
é
\u00e9
Я дважды проверил, что é
персонаж появляется как C3 A9
с помощью hexdump -C ucn.cpp
в соответствии с ожидаемой кодировкой UTF-8. Я также подтвердил, что равнина printf("é\n");
или же printf("\u00e9\n");
работает безупречно, так что это не проблема тестируемых компиляторов, которые не могут прочитать исходные файлы UTF-8.
Кто прав?
‘é’ не является допустимым символом для экранирования обратной косой черты в строковом литерале, поэтому обратная косая черта, за которой следует ‘é’ в качестве литерального исходного символа или UCN, должна вызывать диагностику компилятора и неопределенное поведение.
Обратите внимание, однако, что "\\u00e9"
не является UCN, которому предшествует обратная косая черта, и что невозможно записать любую последовательность основных исходных символов в строке или символьном литерале, которая является обратной косой чертой, за которой следует UCN. таким образом "\é"
а также "\\u00e9"
не обязаны вести себя одинаково: поведение "\\u00e9"
могут быть совершенно четко определены в то время как поведение "\é"
не определено
Если бы мы установили некоторый синтаксис, который позволял бы экранировать обратную косую черту от UCN, скажем, "\«\u00e9»"
, то это будет иметь неопределенное поведение, как "\é"
,
- На первом этапе
printf("\é\n");
карты дляprintf("\\u00e9\n");
,
Первый этап преобразования é
в UCN не может создать не-UCN, такой как "\\u00e9"
,
Компиляторы правы, но специально не справляются с этой ситуацией с помощью совершенных диагностических сообщений. В идеале вы получите:
$ clang++ -std=c++11 -Wall -Wextra ucn.cpp -o ucn
ucn.cpp:4:10: warning: unknown escape sequence '\é' [-Wunknown-escape-sequence]
printf("\é\n");
^
1 warnings generated.
$ ./ucn
é
\u00e9
Оба компилятора указывают, что их поведение в присутствии неизвестной escape-последовательности заключается в замене escape-последовательности на экранированный символ, поэтому "\é"
будет рассматриваться как "é"
и программу в целом следует интерпретировать как:
#include <stdio.h>
int main(void){
printf("é\n");
printf("\\u00e9\n");
return 0;
}
Оба компилятора действительно получают это поведение, частично случайно, но также частично, потому что политика для обработки нераспознанных escape-последовательностей, как они делают, является разумным выбором: даже если они видят только нераспознанную escape-последовательность как обратную косую черту, за которой следует байт 0xC3 они удаляют обратную косую черту и оставляют 0xC3 на месте, что означает, что последовательность UTF-8 остается нетронутой для последующей обработки.
Вы, кажется, смущены, думая, что \\u00e9
это UCN — это не так. UCNs все начинаются с \u
, и в вашем случае у вас есть обратный слэш экстракта, который экранирует этот начальный обратный слеш. Так \\u00e9
это последовательность из 6 символов: \
, u
, 0
, 0
, e
, 9
,
редактировать
На первом этапе printf («\ é \ n»); отображается в printf («\ u00e9 \ n») ;.
Вот где вы идете не так — Фаза 1 переводит входные символы в исходные символы, поэтому printf("\é\n");
карты для p
r
i
n
t
f
(
"
\
é
\
n
"
)
;
, который так же, как p
r
i
n
t
f
(
"
\
\u00e9
\
n
"
)
;
, но это не то же самое, что printf("\\u00e9\n");
сопоставляется из-за двойной обратной косой черты в последнем. Из-за особой обработки двойной обратной косой черты не существует способа, чтобы обратная косая черта сопровождалась UCN в источнике.