Когда речь заходит об окружении, особенно о файлах, стандарт C намеренно становится довольно расплывчатым. В отношении триграфов и кодирования соответствующих им символов даются следующие гарантии:
C11 (n1570) 5.1.1.2 p1 («Этапы перевода») [выделение. мой]
- Многобайтовые символы физического исходного файла сопоставляются способом, определяемым реализацией, с исходным набором символов (введение символов новой строки для индикаторов конца строки), если это необходимо. Последовательности триграфов заменяются соответствующими односимвольными внутренними представлениями.
Таким образом, последовательность триграфа должна быть отображена в один байт. Этот однобайтовый символ должен быть в базовом наборе символов, отличном от любого другого символа в базовом наборе символов. То, как компилятор обрабатывает их внутри во время трансляции, на самом деле не является наблюдаемым поведением, поэтому это не имеет значения.
Если записать в текстовый поток, он может быть преобразован (как я читал, возможно, обратно в последовательность триграфа, если базовая кодировка не имеет кодировки для определенного символа). Его можно прочитать снова, и он должен сравниваться как равный, если он считается печатным символом. Там же. 7.21.2 п2:
[…] Данные, прочитанные из текстового потока, обязательно будут сравниваться с данными, ранее записанными в этот поток, только если: данные состоят только из печатных символов и управляющих символов, горизонтальной табуляции и новой строки; символу новой строки не предшествует пробел; и последний символ является символом новой строки. […]
Там же. 7.4 п3:
Термин «печатный символ» относится к элементу набора символов, зависящих от локали, каждый из которых занимает одну позицию печати на устройстве отображения; термин «управляющий символ» относится к члену набора символов, зависящему от локали, который не является печатным символом.*) Все буквы и цифры являются печатными символами.
*) В реализации, использующей семибитный набор символов US ASCII, печатными являются символы, значения которых лежат в диапазоне от 0x20 (пробел) до 0x7E (тильда); управляющие символы — это символы, значения которых лежат в диапазоне от 0 (NUL) до 0x1F (US), и символ 0x7F (DEL).
И для бинарных потоков там же. 7.21.2 п3:
Двоичный поток — это упорядоченная последовательность символов, которая может прозрачно записывать внутренние данные. Данные, считанные из двоичного потока, должны сравниваться с данными, которые были ранее записаны в этот поток в той же реализации. Однако такой поток может иметь определяемое реализацией количество нулевых символов, добавленных к концу потока.
В комментариях выше возник вопрос, если
printf("int main(void) ??< ??>\n"); // (1)
printf("int main(void) ?\?< ?\?>\n"); // (2)
всегда работает для генерации кода, и вывод этого оператора гарантированно компилируется. Я не смог найти нормативную ссылку, требующую, чтобы isprint('??<')
и т. д. (для (1)
) или даже isprint('<')
и т. д. (для (2)
) возвращали ненулевое значение, но обоснование C89 о потоках гласит:
Набор символов, который необходимо сохранить в текстовом потоке ввода-вывода, необходим для написания программ на C; цель состоит в том, чтобы Стандарт позволял писать транслятор C максимально переносимым способом. Управляющие символы, такие как backspace, для этой цели не требуются, поэтому их обработка в текстовых потоках не обязательна.
Когда '??<'
и т. д. записываются в двоичный поток, он должен отображаться в один байт, печататься как таковой, быть уникальным и отличимым от любого другого базового символа и сравниваться с '??<'
при обратном чтении.
Связано: обоснование C89 о триграфах .
person
mafso
schedule
29.08.2014
{
, но набрать{
с помощью клавиатуры, используемой для написания программы, может быть непросто или даже невозможно. - person Mankarse   schedule 26.08.2014{
или любом другом символе триграфа. Если набор символов известен, можно просто использовать0x7B
или'\x7B
, даже если нельзя набрать{
. А для строковых (по сравнению с символьными) литералов макрос stringize, вероятно, мог бы дать более привлекательные результаты:#define LBR __stringize(??<)
определил быLBR
как"{"
[каким бы ни был символ{
]. - person supercat   schedule 26.08.2014'\x7B'
и'??<'
разные. Первый имеет значение 123, второй код символа{
(который отличается для систем, отличных от ASCII). Вы не могли бы написать переносимый код, выполняющий последнее, без клавиатуры с клавишей{
. - person mafso   schedule 26.08.2014??/
, предоставляют какую-либо функциональность, которую нельзя было бы реализовать, указав файл .h с макросами для символов [например,#define __LBR {
/#define __clbr 0x7B
/#define __SLBR __STRINGIZE(<:)
]? - person supercat   schedule 26.08.2014é
, а 0x7D сè
, и в которой нет имеет глифов для{
и}
, я вижу, чтоint main(void) <: doSomething(); :>
может быть лучше, чемint main(void) é doSomething(); é
. Я предполагаю, что"l'??<l??>ve"
, вероятно, будет отображаться как"l'éléve"
, а не"l'{l}ve"
на такой машине, но я не знаю ничего в спецификации, где бы это говорилось. - person supercat   schedule 26.08.2014printf("int main(void) ??< ??>??/n");
должен компилироваться на этой платформе. - person mafso   schedule 26.08.2014{
, и??<
должен расширяться до этого символа). Если вы сможете найти что-нибудь, что четко указывает на это, и напишите ответ, я приму это. - person supercat   schedule 26.08.2014