Пример чего-то, что является или не является постоянным выражением в C?

Я немного запутался между тем, что является и не является константным выражением в C, даже после долгих поисков в Google. Не могли бы вы привести пример того, что является постоянным выражением в C, а что нет?


person Dave    schedule 20.09.2010    source источник


Ответы (6)


Константное выражение может быть оценено во время компиляции. Это означает, что в нем нет переменных. Например:

5 + 7 / 3

является постоянным выражением. Что-то типа:

5 + someNumber / 3

не является, предполагая, что someNumber является переменной (т. е. сама по себе не является константой времени компиляции).

person Carl Norum    schedule 20.09.2010
comment
Большое спасибо, Карл, очень полезно! :) - person Dave; 21.09.2010
comment
Тем не менее, адрес некоторых переменных может быть в постоянном выражении (те, которые имеют статическую продолжительность хранения). - person caf; 21.09.2010

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

Например, (24*60*60) может быть вычислено обоими, но sizeof struct foo известно только компилятору. Это различие может иметь значение, если вы пытаетесь удостовериться, что struct определено в соответствии с размером, установленным извне, или что его элементы сопоставлены с указанными извне смещениями. (Этот вариант использования часто возникает при кодировании драйверов устройств, где struct описывает регистры устройства, расположенные в пространстве памяти.)

В этом случае вы не можете просто сказать #if (sizeof(struct UART) == 12), потому что препроцессор опережает компиляцию и просто не может знать размер каких-либо типов. Однако это постоянное выражение, и его можно использовать в качестве инициализатора глобальной переменной (например, int UARTwords = sizeof(struct UART) / sizeof(short);) или для объявления размера массива (например, unsigned char UARTmirror[sizeof(struct UART)];).

person RBerteig    schedule 20.09.2010
comment
+1, те, с которыми препроцессор не может справиться, я полагаю, называются «ограниченными постоянными выражениями». - person Carl Norum; 21.09.2010
comment
@Carl: Приятно знать, спасибо. Определены ли ограниченные константные выражения в какой-либо ссылке? Я не нахожу его в стандарте C11. - person Tim; 15.08.2017
comment
Привет @Tim, у меня нет под рукой спецификации, но у Microsoft есть кое-что здесь: docs.microsoft.com/en-ca/cpp/c-language/c-constant-expressions - person Carl Norum; 17.08.2017

Кажется, никто не упомянул еще один вид константных выражений: адресные константы. Адрес объекта со статической продолжительностью хранения является константой адреса, поэтому вы можете делать такие вещи в области файла:

char x;
char *p = &x;

Строковые литералы определяют массивы со статической продолжительностью хранения, поэтому это правило также объясняет, почему вы можете делать это в области файла:

char *s = "foobar";
person caf    schedule 21.09.2010
comment
+1, но следует отметить, что такое постоянное выражение обычно не является фактической константой во время компиляции и, возможно, не во время компоновки, если вы используете динамическую компоновку. В большинстве реальных реализаций это приводит к перемещению. - person R.. GitHub STOP HELPING ICE; 21.09.2010
comment
Это то, что я искал! - person abhiarora; 18.03.2017

Любой однозначный литерал является константным выражением.

3     0.0f    '\n'

(Строковые литералы странные, потому что они на самом деле являются массивами. Кажется, "hello" на самом деле не является константой, так как в конечном итоге ее нужно связать и все такое, а адрес и содержимое могут измениться во время выполнения.)

Большинство операций (sizeof, casts и т. д.), применяемых к константам или типам, являются константными выражениями.

sizeof(char)
(byte) 15

Любое выражение, включающее только константные выражения, само также является константным выражением.

15 + 3
0.0f + 0.0f
sizeof(char)

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

strlen("hello")
fifteen + x

Статус любого макроса как постоянного выражения зависит от того, до чего он расширяется.

/* Always a constant */
#define FIFTEEN 15

/* Only constant if (x) is
#define htons(x)  (( ((x) >> 8) | ((x) << 8) ) & 0xffff) 

/* Never constant */
#define X_LENGTH  strlen(x)

Первоначально у меня было кое-что об идентификаторах const, но я проверил это, и, по-видимому, это не применимо в C. const, как ни странно, не объявляет константы (по крайней мере, не "достаточно константные", чтобы их можно было использовать в switch заявления). Однако в C++ это так.

person cHao    schedule 20.09.2010
comment
Обратите внимание, что sizeof() всегда является константным выражением, даже если аргумент не является константным выражением, поскольку это тип выражения, которое является аргументом, а не само выражение. - person Clifford; 21.09.2010
comment
@Clifford: в C99 sizeof, примененный к VLA, не указывает константу 6.5.3.4 ... Если тип операнда является типом массива переменной длины, операнд оценивается; в противном случае операнд не оценивается и результатом является целочисленная константа. - person pmg; 21.09.2010
comment
@cHao: const - это квалификатор типа. Это относится к объектам... и ни один объект не может быть константой. - person pmg; 21.09.2010
comment
Хм, я немного сомневаюсь, что строковые литералы являются постоянными выражениями. Они имеют тип char[], вы можете взять их адрес и применить к ним оператор [], например, "Hello"[1] является допустимым выражением. - person Jens Gustedt; 21.09.2010
comment
@pmg: Если вы говорите const int zero = 0, то switch (x) { case zero: break; default: break; } компилируется на C++. Однако это все еще недействительно C. - person cHao; 21.09.2010
comment
Да, @cHao... C и C++ имеют свои отличия. В C ваш zero выше — это, прежде всего, объект. Вы можете взять его адрес, применить к нему sizeof, ... (не знаю, сможете ли вы сделать это в C++) - person pmg; 21.09.2010
comment
@pmg: Похоже на то. В C++ zero действует как реальная переменная и константное выражение. Я могу взять его адрес, применить sizeof, все, кроме изменения его значения. Даже это можно сделать в какой-то степени, но я бы не рассчитывал на то, что это не испортит все. (Компилятор, вероятно, жестко закодировал 0 вместо zero везде, где ему нужна была константа.) - person cHao; 21.09.2010
comment
А как насчет const volatile int fixed = 42;? :) - person pmg; 21.09.2010
comment
@pmg, многие регистры устройств ввода-вывода правильно объявлены const volatile. const указывает на регистр только для чтения. volatile указывает на регистр, значение которого может изменяться вне потока управления этой единицей компиляции, и аппаратный регистр состояния является прекрасным примером такого значения. Вся хитрость заключается в том, чтобы определить (и связать) объявление регистра, чтобы он, конечно, был помещен на фактическое устройство ввода-вывода. - person RBerteig; 21.09.2010
comment
Я имею в виду: можно ли использовать fixed, как в const volatile int fixed = 42;, константу C++, например, в качестве метки case в операторе switch? (У меня здесь нет компилятора С++, иначе я бы его протестировал... и мне не хочется искать спецификацию С++ и просматривать ее) - person pmg; 21.09.2010
comment
volatile const на самом деле не являются константами — это просто переменные только для чтения. Так что нет, они не будут рассматриваться как константы. Я почти уверен, что extern вещи тоже имеют некоторые странные правила, особенно если они не объявлены и не установлены в одно и то же время. - person cHao; 21.09.2010

Еще одна забавная маленькая загвоздка: в C значение "enum" является константой, но может использоваться только после того, как объявление "enum" завершено. Например, следующее неприемлемо в стандартном C, хотя допустимо в C++:

enum {foo=19, bar, boz=bar+5;};

Его можно было бы переписать:

enum {foo=19, bar}; enum {boz=bar+5;};

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

person supercat    schedule 20.09.2010
comment
Просто упомянем, что эти enum константы имеют тип int. - person Jens Gustedt; 21.09.2010

Также integral character constants как 'a' или '\n' являются константами, которые компилятор распознает как таковые. Они имеют тип int.

person Jens Gustedt    schedule 20.09.2010