Условное включение: числовое значение символьных констант: внутри #if/#elif и без #if/#elif: почему сопоставление определяется реализацией?

Случай A: C11, 6.6 Постоянные выражения, семантика, 5:

Если плавающее выражение оценивается в среде перевода, арифметический диапазон и точность должны быть как минимум такими же большими, как если бы выражение оценивалось в среде выполнения.116)

который требует, чтобы следующая программа возвращала 0:

      #include <float.h>

#define EXPR DBL_MIN * DBL_MAX

double d1 = EXPR;
double d2;

#pragma STDC FENV_ACCESS ON

int main(void)
{
    d2 = EXPR;
    return d1 == d2 ? 0 : 1;
}

Случай B: C11, 6.10.1 Условное включение, семантика, 4:

Соответствие числового значения этих символьных констант значению, полученному, когда идентичная символьная константа встречается в выражении (кроме директив #if или #elif), определяется реализацией.168)

который не требует, чтобы следующая программа возвращала 0:

      #define EXPR 'z' - 'a' == 25

int main(void)
{
    _Bool b1 = 0;
    _Bool b2;
#if EXPR
    b1 = 1;
#endif
    b2 = EXPR;
    return b1 == b2 ? 0 : 1;
}

Вопрос: каково обоснование создания поведения, определяемого реализацией, "Case B"?

1 ответ

Стандарт C11 (я буду цитировать этот проект документа ) определяет два набора символов:

5.2.1 Наборы символов

1 Должны быть определены два набора символов и связанные с ними последовательности сопоставления: набор, в котором записываются исходные файлы ( исходный набор символов), и набор, интерпретируемый в среде выполнения ( набор символов выполнения). Каждый набор далее делится на базовый набор символов, содержание которого дается в этом подпункте, и набор из нуля или более членов, специфичных для локали (которые не являются элементами основного набора символов), называемых расширенными символами. Комбинированный набор также называется расширенным набором символов . Значения членов набора символов выполнения определяются реализацией.

Кроме того, нет требования, чтобы эквивалентные символы в этих наборах были представлены одними и теми же значениями, и нет требования, чтобы латинские буквы хранились в определенной последовательности. Так, в приведенном примере значение не обязательно должны быть одинаковыми в этих двух наборах.

Теперь порядок фаз трансляции указывает, что вызовы макросов и оценки (и другие директивы предварительной обработки) выполняются с использованием исходного набора символов, но выражения, которые встречаются в исполняемом коде, оцениваются после преобразования в набор символов выполнения:

5.1.1.2 Фазы перевода


4. Директивы предварительной обработки выполняются, вызовы макросов расширяются, и _Pragmaвыполняются унарные операторные выражения. Если последовательность символов, соответствующая синтаксису универсального имени символа, создается конкатенацией маркеров (6.10.3.3), поведение не определено. А #includeдиректива предварительной обработки вызывает рекурсивную обработку именованного заголовка или исходного файла с фазы 1 по фазу 4. Затем все директивы предварительной обработки удаляются.
5. Каждый элемент исходного набора символов и управляющая последовательность в символьных константах и ​​строковых литералах преобразуются в соответствующий член набора символов выполнения; если соответствующий член отсутствует, он преобразуется в определяемый реализацией член, отличный от нулевого (широкого) символа.

Таким образом, поскольку взаимосвязь между этими наборами символов определяется реализацией, и поскольку два экземпляра константных выражений на основе символов определены для использования разных наборов, тот факт, что они могут иметь разные оценки , также должен определяться реализацией.

Другие вопросы по тегам