Почему большинство разработчиков C используют определение вместо const?
Во многих программах #define
выполняет ту же задачу, что и константа. Например.
#define FIELD_WIDTH 10 const int fieldWidth = 10;
Я обычно вижу, что первая форма предпочтительнее другой, полагаясь на предварительный процессор для обработки того, что в основном является решением приложения. Есть ли причина для этой традиции?
- Что означает «#define STR (a) #a»?
- Как сделать строку char из значения макроса C?
- Как сделать переменный макрос (переменное количество аргументов)
- Как я могу увидеть исходный файл C / C ++ после предварительной обработки в Visual Studio?
- В чем разница между #define и const?
- Почему #ifndef и #define используются в файлах заголовков C ++?
- «#Include» текстовый файл в программе на языке C как символ
- Когда macros C ++ полезны?
- Можно ли использовать препроцессор C, чтобы определить, существует ли файл?
- Компилировать, создавать или архивировать проблемы с Xcode 4 (и зависимостями)
- Почему macros препроцессора злые и какие альтернативы?
- Как я могу генерировать уникальные значения в препроцессоре C?
- Должен ли я использовать константы над определениями?
Для этого есть очень веская причина: const
в C не означает, что что-то постоянно. Это просто означает, что переменная доступна только для чтения.
В тех местах, где для компилятора требуется истинная константа (например, для размеров массива для массивов, отличных от VLA), использование константной переменной, такой как fieldWidth
, просто невозможно.
Они разные.
const
– это просто определитель, который говорит, что переменную нельзя изменять во время выполнения. Но все остальные функции переменной сохраняются: она выделяет хранилище, и это хранилище может быть устранено. Таким образом, код не просто рассматривает его как литерал, а ссылается на переменную, обращаясь к указанной ячейке памяти (за исключением того, что она является static const
, затем ее можно оптимизировать) и загружать ее значение во время выполнения. И поскольку const
переменная выделяет хранилище, если вы добавляете его в заголовок и включаете его в несколько источников C, вы получите сообщение об ошибке «множественное определение символа», если вы не отметите его как extern
. И в этом случае компилятор не может оптимизировать код по сравнению с его фактическим значением (если не включена глобальная оптимизация).
#define
просто заменяет имя с его значением. Кроме того, в препроцессоре может использоваться константа #define
‘d: вы можете использовать ее с #ifdef
для выполнения условной компиляции на основе ее значения или использовать оператор стробирования #
чтобы получить строку со своим значением. И поскольку компилятор знает свое значение во время компиляции, он может оптимизировать код на основе этого значения.
Например:
#define SCALE 1 ... scaled_x = x * SCALE;
Когда SCALE
определяется как 1
компилятор может устранить умножение, поскольку он знает, что x * 1 == x
, но если SCALE
является ( extern
) const
, ему необходимо сгенерировать код для извлечения значения и выполнить умножение, поскольку значение не будут известны до стадии связывания. ( extern
необходим для использования константы из нескольких исходных файлов.)
Более близким эквивалентом использования #define
является использование перечислений:
enum dummy_enum { constant_value = 10010 };
Но это ограничено целыми значениями и не имеет преимуществ #define
, поэтому оно широко не используется.
const
полезно, когда вам нужно импортировать постоянное значение из какой-либо библиотеки, где она была скомпилирована. Или если она используется с указателями. Или, если это массив постоянных значений, доступных через переменное значение индекса. В противном случае const
не имеет преимуществ перед #define
.
Причина в том, что большую часть времени вы хотите константу, а не const
-qualified. Эти два не являются удаленно одинаковыми на языке C. Например, переменные недействительны как часть инициализаторов для объектов static
-storage-duration, как размеры массива не-vla (например, размер массива в структуре или любой массив pre-C99).
Расширение ответа на R немного: fieldWidth
не является постоянным выражением ; это const
-qualified variable. Его значение не устанавливается до времени выполнения, поэтому его нельзя использовать там, где требуется выражение постоянной времени компиляции (например, в объявлении массива или метка case в инструкции switch
и т. Д.).
Сравните с макросом FIELD_WIDTH
, который после предварительной обработки расширяется до константного выражения 10
; это значение известно во время компиляции, поэтому его можно использовать для размеров массива, меток ярлыков и т. д.
Чтобы добавить к ответу Р. и Барта: есть только один способ определить константы времени символьной компиляции в константах типа C: enumeration. Стандарт устанавливает, что они имеют тип int
. Я лично напишу ваш пример как
enum { fieldWidth = 10 };
Но я думаю, что этот вкус сильно отличается среди программистов C по этому поводу.
Хотя const int не всегда будет подходящим, перечисление обычно будет работать как замена для #define, если вы определяете что-то целостное значение. На самом деле это предпочтение мне в таком случае.
enum { FIELD_WIDTH = 16384 }; char buf[FIELD_WIDTH];
В C ++ это огромное преимущество, так как вы можете объединить свое перечисление в class или пространство имен, в то время как вы не можете обладать #define.
В C у вас нет пространств имен и невозможно охватить перечисление внутри структуры, и я даже не уверен, что вы получите безопасность типа, поэтому я не вижу на самом деле какого-либо главного преимущества, хотя, возможно, какой-то программист на C мне укажет на это ,
Согласно K & R (2-е издание, стр. 211), «const и volatile properties являются новыми с ANSI-стандартом». Это может означать, что действительно старый код ANSI вообще не имел этих ключевых слов, и это действительно вопрос традиции. Более того, в нем говорится, что компилятор должен обнаруживать попытки изменить константные переменные, но кроме этого он может игнорировать эти квалификаторы. Я думаю, это означает, что некоторые компиляторы не могут оптимизировать код, содержащий константную переменную, которая будет представлена как промежуточное значение в машинных кодах (например, #define), и это может стоить в дополнительное время для доступа к большой памяти и влияния на производительность.
Некоторые компиляторы C будут хранить все const
переменные в двоичном формате, которые при подготовке большого списка коэффициентов могут использовать огромное количество пространства во встроенном мире.
И наоборот: использование const
позволяет мигать над существующей программой для изменения определенных параметров.
Лучший способ определения числовых констант в C – использование enums . Прочтите соответствующую главу «Язык программирования C & R» на стр. 39.