Я немного запутался между тем, что является и не является константным выражением в C, даже после долгих поисков в Google. Не могли бы вы привести пример того, что является постоянным выражением в C, а что нет?
Пример чего-то, что является или не является постоянным выражением в C?
Ответы (6)
Константное выражение может быть оценено во время компиляции. Это означает, что в нем нет переменных. Например:
5 + 7 / 3
является постоянным выражением. Что-то типа:
5 + someNumber / 3
не является, предполагая, что someNumber
является переменной (т. е. сама по себе не является константой времени компиляции).
Есть еще одна тонкость константных выражений. Есть некоторые вещи, которые известны компилятору, но не могут быть известны препроцессору.
Например, (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)];
).
Кажется, никто не упомянул еще один вид константных выражений: адресные константы. Адрес объекта со статической продолжительностью хранения является константой адреса, поэтому вы можете делать такие вещи в области файла:
char x;
char *p = &x;
Строковые литералы определяют массивы со статической продолжительностью хранения, поэтому это правило также объясняет, почему вы можете делать это в области файла:
char *s = "foobar";
Любой однозначный литерал является константным выражением.
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++ это так.
C99
sizeof, примененный к VLA, не указывает константу 6.5.3.4 ... Если тип операнда является типом массива переменной длины, операнд оценивается; в противном случае операнд не оценивается и результатом является целочисленная константа.
- person pmg; 21.09.2010
const
- это квалификатор типа. Это относится к объектам... и ни один объект не может быть константой.
- person pmg; 21.09.2010
char[]
, вы можете взять их адрес и применить к ним оператор []
, например, "Hello"[1]
является допустимым выражением.
- person Jens Gustedt; 21.09.2010
const int zero = 0
, то switch (x) { case zero: break; default: break; }
компилируется на C++. Однако это все еще недействительно C.
- person cHao; 21.09.2010
C
и C++
имеют свои отличия. В C
ваш zero
выше — это, прежде всего, объект. Вы можете взять его адрес, применить к нему sizeof
, ... (не знаю, сможете ли вы сделать это в C++
)
- person pmg; 21.09.2010
zero
действует как реальная переменная и константное выражение. Я могу взять его адрес, применить sizeof
, все, кроме изменения его значения. Даже это можно сделать в какой-то степени, но я бы не рассчитывал на то, что это не испортит все. (Компилятор, вероятно, жестко закодировал 0 вместо zero
везде, где ему нужна была константа.)
- person cHao; 21.09.2010
const volatile int fixed = 42;
? :)
- person pmg; 21.09.2010
const volatile
. const
указывает на регистр только для чтения. volatile
указывает на регистр, значение которого может изменяться вне потока управления этой единицей компиляции, и аппаратный регистр состояния является прекрасным примером такого значения. Вся хитрость заключается в том, чтобы определить (и связать) объявление регистра, чтобы он, конечно, был помещен на фактическое устройство ввода-вывода.
- person RBerteig; 21.09.2010
fixed
, как в const volatile int fixed = 42;
, константу C++
, например, в качестве метки case в операторе switch? (У меня здесь нет компилятора С++, иначе я бы его протестировал... и мне не хочется искать спецификацию С++ и просматривать ее)
- person pmg; 21.09.2010
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;};
хотя в конечном итоге это приведет к определению нескольких разных типов перечисления, а не одного, который содержит все значения.
enum
константы имеют тип int
.
- person Jens Gustedt; 21.09.2010
Также integral character constants
как 'a'
или '\n'
являются константами, которые компилятор распознает как таковые. Они имеют тип int
.