Детали того, что представляет собой постоянное выражение В C?

C определяет по крайней мере 3 уровня "постоянного выражения":

  • постоянное выражение (неквалифицированное)
  • арифметическое постоянное выражение
  • целочисленное константное выражение

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

значит 1,2 не является постоянным выражением?

пункт 8 гласит:

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

каковы операнды в (union { uint32_t i; float f; }){ 1 }.f? Если 1 является операндом, то это, по-видимому, арифметическое постоянное выражение, но если { 1 } это операнд, тогда это явно не так.

Edit: еще одно интересное замечание: 7.17 пункт 3 требует результата offsetof быть целочисленным постоянным выражением типа size_t, но стандартные реализации из offsetof, насколько я могу судить, не обязательно быть целочисленными постоянными выражениями по стандарту. Это, конечно, нормально, поскольку реализация разрешена (в соответствии с пунктом 6.6 10) принимать другие формы постоянных выражений или реализовывать offsetof макрос as __builtin_offsetof, а не через вычитание указателя. Суть этого наблюдения в том, что если вы хотите использовать offsetof в контексте, где требуется целочисленное постоянное выражение, вам действительно нужно использовать предоставленный макрос по реализации и не сворачивайте самостоятельно.

2 ответов


основываясь на вашем чтении,1,2 не является постоянным выражением. Я не знаю, почему это не так, просто я согласен с вами, что это не так (несмотря на то, что это, вероятно, должно быть).

6.5.2 указывает составные литералы в качестве постфиксного оператора. Так

(union { uint32_t i; float f; }){ 1 }.f

операнды (union { uint32_t i; float f; }){ 1 } и f до . оператора. Это не арифметическое постоянное выражение, так как первый аргумент является union тип, но это постоянная выражение.

обновление: я основывал это на другой интерпретации стандарта.

моим предыдущим рассуждением было то, что (union { uint32_t i; float f; }){ 1 }.f соответствует критериям для постоянного выражения и поэтому является постоянным выражением. Я все еще думаю, что он соответствует критериям для постоянного выражения (6.6 пункт 3), но это не какой-либо из стандартных типов постоянных выражений (целое число, арифметика или адрес) и поэтому только подлежит константе выражение по 6.6 параграф 10, который позволяет реализовать определенные постоянные выражения.

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


Если 1,2 было бы постоянным выражением, это позволило бы скомпилировать такой код:

{ // code        // How the compiler interprets:
  int a[10, 10]; // int a[10];

  a[5, 8] = 42;  // a[8] = 42;
}

Я не знаю, является ли это реальной причиной, но я могу себе представить, что испуская ошибку для этого (общий?) ошибка считалась более важной, чем поворот 1,2 в постоянное выражение.

обновление: как указывает R. В комментарии, код больше не является ошибкой компилятора с момента введения VLAs.