Подробности того, что составляет постоянное выражение в C?
-
28-10-2019 - |
Вопрос
C определяет как минимум 3 уровня «постоянного выражения»:
- постоянное выражение (неквалифицированное)
- арифметическое постоянное выражение
- целочисленное постоянное выражение
6.6 абзац 3 гласит:
<цитата>Постоянные выражения не должны содержать присваивание, приращение, декремент, вызов функции, или операторы запятой, кроме случаев, когда они содержатся в подвыражении, которое не оценено.
Значит ли это, что 1,2
не является постоянным выражением?
Пункт 8 гласит:
<цитата>Выражение арифметической константы должно иметь арифметический тип и иметь только операнды, которые являются целочисленными константами, плавающими константами, константами перечисления, символом константы и выражения sizeof. Операторы приведения в арифметическом постоянном выражении должен преобразовывать только арифметические типы в арифметические типы, кроме как части операнда в оператор sizeof, результатом которого является целочисленная константа.
Каковы операнды в коде (union { uint32_t i; float f; }){ 1 }.f
? Если 1
является операндом, то это предположительно арифметическое постоянное выражение, но если { 1 }
является операндом, то это явно не так.
Изменить: Еще одно интересное наблюдение: пункт 3 7.17 требует, чтобы результат offsetof
был целочисленным постоянным выражением типа size_t
, но стандартные реализации offsetof
, насколько я могу судить, не Стандарт должен быть целочисленным постоянным выражением. Это, конечно, нормально, поскольку реализации разрешено (в соответствии с параграфом 10 6.6) принимать другие формы константных выражений или реализовывать макрос offsetof
как __builtin_offsetof
, а не через вычитание указателя. Однако суть этого наблюдения заключается в том, что если вы хотите использовать offsetof
в контексте, где требуется целочисленное постоянное выражение, вам действительно нужно использовать макрос, предоставленный реализацией, а не использовать собственный.
Решение
Судя по вашему чтению, 1,2
не является постоянным выражением. Я не знаю, почему это не так, просто я согласен с вами, что это не так (несмотря на то, что, вероятно, так и должно быть).
6.5.2 определяет составные литералы как постфиксный оператор. Итак, в
родовое слово Операнды - это (union { uint32_t i; float f; }){ 1 }
и f
для оператора .
. Это не арифметическое постоянное выражение, поскольку первый аргумент является типом кода union
, но это постоянное выражение.
ОБНОВЛЕНИЕ: я основывал это на другой интерпретации стандарта.
Мое предыдущее рассуждение заключалось в том, что (union { uint32_t i; float f; }){ 1 }.f
удовлетворяет критериям постоянного выражения и, следовательно, является постоянным выражением. Я по-прежнему считаю, что оно соответствует критериям константного выражения (6.6 абзац 3), но не является ни одним из стандартных типов константных выражений (целочисленные, арифметические или адресные) и, следовательно, может быть константным выражением только в абзаце 6.6. 10, что позволяет использовать константные выражения, определяемые реализацией.
Я тоже хотел перейти к вашей редакции. Я собирался возразить, что «взломанная» реализация offsetof
была константным выражением, но я думаю, что это то же самое, что и выше: оно соответствует критериям для постоянного выражения (и, возможно, константы адреса), но не является целочисленным постоянным выражением, и поэтому недействителен за пределами пункта 10 6.6.
Другие советы
Если бы 1,2
было постоянным выражением, это позволило бы компилировать такой код:
Я не знаю, истинная ли это причина, но могу представить, что выдача ошибки для этой (распространенной?) ошибки считалась более важной, чем превращение кода 1,2
в постоянное выражение.
ОБНОВЛЕНИЕ : как указывает R. в комментарии, код больше не является ошибкой компилятора с момента появления VLA.