C11 и оценка константного выражения в метках переключения

После этого вопроса Почему gcc не допускает использование const int в качестве выражения case?, в основном так же, как Какие продвигаемые типы используются для сравнения выражений регистра или есть ли способ использовать постоянный массив с постоянным индексом в качестве метки регистра переключателя в C?,

Из первой ссылки я попытался заменить:

case FOO:                       // aka 'const int FOO = 10'

с:

case ((int) "toto"[0]):         // can't be anything *but* constant

Который дает:

https://ideone.com/n1bmIb -> https://ideone.com/4aOSXR = работает в C++

https://ideone.com/n1bmIb -> https://ideone.com/RrnO2R = не удается в C

Я не совсем понимаю, поскольку строка "toto" не может быть ничем иным, как константой, это даже не переменная, она находится в пустоте памяти компилятора. Я даже не играю с нечеткой логикой "const" языка Си (которая на самом деле означает "только для чтения, а не константа, что вы ожидали?"), Проблема в "доступе к массиву" или "обращении к указателю" "в константное выражение, которое не оценивается в C, но хорошо работает в C++.

Я ожидал использовать этот "трюк" для использования HASH_MACRO(str) для генерации уникальных значений меток наблюдений из ключевого идентификатора, в результате чего компилятор в конечном итоге выдает ошибку в случае коллизии из-за найденных сходных значений меток.

Хорошо, хорошо, мне сказали, что эти ограничения были сделаны для упрощения языкового инструментария (preproc, компилятор, компоновщик) и C не являются LISP, но вы можете иметь полнофункциональные интерпретаторы / компиляторы LISP для доли размера эквивалента C так что это не оправдание.

Вопрос: есть ли "расширение" до C11, которое просто позволяет этому "toto" работать в GCC, CLANG и... MSVC? Я не хочу идти по пути C++ (предварительные объявления typedef больше не работают) и потому что встроенные вещи (отсюда вычисление хеша во время компиляции для пространственно-временного искажения).

Есть ли посреднический язык "C+", который более "разрешительный" и "понимающий", встроенный немного лучше, как, например, "Прославить лордов", "перечисляет как члены битового поля", среди прочего, мы не можем иметь (из-за стандарты реальности развиваются как улитки под солнцем пустыни)?

#provemewrong, #changemymind, #norustplease

3 ответа

Не имеет значения, может ли он быть известен компилятору во время компиляции. case метка должна иметь значение, являющееся целочисленным константным выражением (C11 6.8.4.2p3).

  1. Выражение каждого case метка должна быть целочисленным константным выражением, и никакие два выражения констант в одном и том же операторе switch не должны иметь одинакового значения после преобразования В операторе switch может быть не более одной метки по умолчанию. (Любой вложенный оператор switch может иметь метку по умолчанию или выражения константы регистра со значениями, которые дублируют выражения константы регистра во включающем операторе switch.)

И определение целочисленного константного выражения находится в C11 6.6p6:

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

поскольку "toto" не является целочисленной константой, константами перечисления, символьными константами, константой sizeof, _Alignof выражения или константа с плавающей точкой приводятся к целому числу; и этот список был указан в разделе ограничений стандарта, компилятор не должен передавать это молча. (Даже соответствующий компилятор все еще может успешно компилировать программу, но он должен диагностировать это как нарушение ограничения.)


То, что вы можете использовать, приковано ? : разрешить индекс до символьной константы, т.е.

  x == 0 ? 't' 
: x == 1 ? 'o'
: x == 2 ? 't'
: x == 3 ? 'o'

Это можно записать в макрос.

"toto[0]" не является целочисленным константным выражением, так как C определяет термин:

6.6 Постоянные выражения
...
6 Целочисленное константное выражение 117) должно иметь целочисленный тип и иметь только те операнды, которые являются целочисленными константами, константами перечисления, символьными константами, sizeof выражения, результаты которых являются целочисленными константами, _Alignof выражения и плавающие константы, которые являются непосредственными операндами приведений. Операторы приведения в выражении с целочисленной константой должны преобразовывать только арифметические типы в целочисленные типы, кроме как как часть операнда в sizeof или же _Alignof оператор.
117) Выражение целочисленной константы требуется в ряде контекстов, таких как размер элемента битового поля структуры, значение константы перечисления и размер массива неизменной длины. Дополнительные ограничения, которые применяются к целочисленным константным выражениям, используемым в директивах предварительной обработки условного включения, обсуждаются в 6.10.1.

C 2011 онлайн проект

Проблема, с которой вы сталкиваетесь, заключается в том, что в C "toto" представляет собой массив символов. Конечно, он постоянен в памяти, но это все еще просто массив. Оператор [] индексирует в массиве (по указателю). Если бы вы хотели, вы могли бы редактировать скомпилированный двоичный файл и изменить строку "toto" на что-то еще. В некотором смысле, это не известно во время компиляции. Это эквивалентно выполнению:

char * const ___string1 = "toto";
...
case ((int) ___string1[0]):

(Это немного принудительно и излишне, но это только для демонстрации)

Обратите внимание, что тип элементов строкового литерала charне const char,

Однако регистр должен быть константой, поскольку он встроен в поток управления скомпилированной программы.

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