О литье в целочисленном постоянном выражении (в стандарте C)

В стандарте C (я имею в виду C99 или C11) мы имеем так называемые целочисленные константные выражения , которые являются постоянными выражениями, операндами которых являются все постоянные целые числа. Существуют и другие ограничения, чтобы избежать использования операторов запятой в выражении.

Однако в некоторых особых случаях допускаются другие нецелые объекты (даже непостоянные).
Например, если оператор sizeof применяется к объекту, размер которого известен во время перевода, это разрешено как часть целочисленного постоянного выражения (обратите внимание, что sizeof всегда возвращает целочисленное значение).

Кроме того, иногда допускается явное приведение к целочисленному типу.
Стандарт C99 устанавливает следующее правило:

Стандарт C99, раздел 6.6 (п. 6):

Целочисленное константное выражение) должен иметь целочисленный тип и должен иметь только операнды, которые являются целыми суммами, константами enums, символьными константами, sizeof выражениями, результаты которых являются целыми константами и плавающими константами, которые являются непосредственными операндами приведения.

Стандартный C99

Мой вопрос: каково точное значение «плавающих констант, которые являются непосредственными операндами приписей»?

Плавающая константа – это что-то вроде 3.14e + 3 или хорошо 0x2.11p-5.
То есть, это не общее постоянное выражение типа float, а только литерал с плавающей запятой.
Затем я понимаю, что в приведенном выше определении допускается только что-то подобное:

  (int) 3.14 

но не допускаются операции с использованием плавающих литералов.
Это исключает следующие случаи:

  (int) -3.14 /* The minus sign is not part of the constant: it is an operator */ (int) (3.14) /* The parenthesis are an operator acting on the literal 3.14 */ 

Последнему случаю не нужно выполнять какие-либо арифметические операции с плавающей запятой во время перевода, и это эквивалентно тому, что они не имеют скобок: (int) 3.14 .
Однако он не является непосредственным операндом актера .

Итак, нужно ли нам считать, что (int) (3.14) является [частью] допустимым целочисленным постоянным выражением или нет (согласно определению)?

С другой стороны, компилятор GCC (с опциями: -std = c99 -pedantic-errors) дает мне, что (int) (3.14) является допустимым целочисленным постоянным выражением , например, в объявлении следующим образом:

  #define BITW (int) (3.14) struct { unsigned bitfield: BITW } test; // Translation success 

(Однако, делая #define BITW (int) (+3.14) он не может перевести, как и ожидалось).

Хотя плохой выбор формулировки может означать, что (int) (3.14) не квалифицируется как целочисленное постоянное выражение, я думаю, что это просто ошибка в формулировке. Существует аналогичная проблема, которая, возможно, является ошибкой со спецификацией NULL : ей разрешена любая константа нулевого указателя, где константа нулевого указателя определяется как:

  1. целочисленное постоянное выражение со значением 0 или
  2. такое выражение, отлитое от void * .

Однако, чтобы быть полезным в качестве определения для NULL , выражение должно быть правильно заключено в скобки; все существующие реализации, о которых я знаю, делают это. Но строгим чтением, в то время как (void *)0 является константой нулевого указателя, ((void *)0) не является (это приложение оператора скобок к константе нулевого указателя).

В идеале язык должен быть добавлен где-то в спецификации, поясняя, что круглые скобки не влияют на такие вещи.

Для получения информации gcc-разработчик и разработчик Джозеф Майерс обсудили свою личную страницу о проблемах с постоянными выражениями в стандарте C99. Исходный текст появился на comp.std.c 10 лет назад, но был расширен позже.

http://www.polyomino.org.uk/computer/c/const-exprs-issues.txt

Среди вопросов с постоянными выражениями, которые он поднял, те, которые вы описали, обсуждаются в его пункте (5):

(5) Каковы «операнды» (6.6 абзацы 6 и 8) постоянного выражения? Предположительно это означает «конечные» операнды в некотором смысле, рекурсивные различные операторы к их операндам, но это не указано. Предположительно, сложные литералы, такие как (const int) {0}, не предназначены для постоянных выражений (являющихся ссылками на анонимные переменные), но из текста вряд ли ясно, что здесь 0 не является операндом. Когда составные литералы появляются в выражениях размера, результаты которых не являются целыми константами в неоцененных частях выражений, могут ли выражения выражения арифметической константы быть в зависимости от того, какие отливки присутствуют в составных литералах. Кроме того, можно было бы ожидать, что скобки должны быть чисто синтаксическими, а не иметь «операнды», так что (int) (0.0) является целочисленным постоянным выражением так же, как (int) 0.0 is, и ((void *) 0) является константу нулевого указателя, но об этом нигде не сказано.

На его личной странице, ссылающейся на этот текст, написано:

У меня также есть обсуждение вопросов с постоянными выражениями (не в форме предшествующих DRs, части могут стать DR после реализации).

Насколько мне известно, терминологический вопрос, поднятый в пункте (5), еще не был поднят в DR.