Почему большинство разработчиков C используют определение вместо const?

Во многих программах #define выполняет ту же задачу, что и константа. Например.

 #define FIELD_WIDTH 10 const int fieldWidth = 10; 

Я обычно вижу, что первая форма предпочтительнее другой, полагаясь на предварительный процессор для обработки того, что в основном является решением приложения. Есть ли причина для этой традиции?

Для этого есть очень веская причина: 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.

  • Должен ли я использовать #define, enum или const?
  • Как определить символ препроцессора в Xcode
  • Как проверить ОС с помощью директивы препроцессора?
  • Можем ли мы иметь рекурсивные macros?
  • C ++ #include семантика
  • Макро против функции в C
  • Легкий способ использовать переменные типов enums как строку в C?
  • #pragma pack effect
  • Определения макросов C # в препроцессоре
  • Условная компиляция в зависимости от версии фреймворка в C #
  • Номер строки C / C ++
  • Давайте будем гением компьютера.