Почему большинство разработчиков C используют определение вместо const?
Во многих программах a #define
выполняет ту же задачу, что и константа. Например.
#define FIELD_WIDTH 10
const int fieldWidth = 10;
Я обычно вижу, что первая форма предпочтительнее другой, полагаясь на предварительный процессор для обработки того, что в основном является решением приложения. Есть ли причина для этой традиции?
Ответы
Ответ 1
Существует очень веская причина: const
в C не означает, что что-то постоянно. Это просто означает, что переменная доступна только для чтения.
В тех местах, где для компилятора требуется истинная константа (например, для размеров массива для массивов, отличных от VLA), использование переменной const
, например fieldWidth
, просто невозможно.
Ответ 2
Они разные.
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
.
Ответ 3
Причина в том, что большую часть времени вам нужна константа, а не const
-qualified. Эти два не являются удаленно одинаковыми на языке C. Например, переменные недействительны как часть инициализаторов для объектов static
-storage-duration, как размеры массива не-vla (например, размер массива в структуре или любой массив pre-C99).
Ответ 4
Развернуть на R немного: fieldWidth
не является постоянным выражением; это a const
-qualified variable. Его значение не устанавливается до времени выполнения, поэтому оно не может использоваться там, где требуется выражение константы времени компиляции (например, в объявлении массива или в ярлыке case в инструкции switch
и т.д.).
Сравните с макросом FIELD_WIDTH
, который после предварительной обработки расширяется до константного выражения 10
; это значение известно во время компиляции, поэтому его можно использовать для размеров массива, меток ярлыков и т.д.
Ответ 5
Чтобы добавить к R. и Bart ответ: существует только один способ определения констант времени символьной компиляции в константах типа C: enumeration. Стандарт устанавливает, что они имеют тип int
. Я лично напишу ваш пример как
enum { fieldWidth = 10 };
Но я предполагаю, что этот вкус сильно отличается среди программистов C о нем.
Ответ 6
Хотя константа int не всегда будет подходящей, перечисление будет обычно работать как замена для #define, если вы определяете что-то целостное значение. Это на самом деле мое предпочтение в таком случае.
enum { FIELD_WIDTH = 16384 };
char buf[FIELD_WIDTH];
В С++ это огромное преимущество, поскольку вы можете объединить свое перечисление в класс или пространство имен, в то время как вы не можете обладать #define.
В C у вас нет пространств имен и невозможно охватить перечисление внутри структуры, и я даже не уверен, что вы получите безопасность типа, поэтому я не вижу на самом деле какого-либо главного преимущества, хотя, возможно, какой-то программист C там укажет для меня.
Ответ 7
Согласно K & R (2-е издание, стр. 211), "константные и летучие свойства являются новыми со стандартом ANSI". Это может означать, что действительно старый код ANSI вообще не имел этих ключевых слов, и это действительно вопрос традиции.
Более того, в нем говорится, что компилятор должен обнаруживать попытки изменить константные переменные, но кроме этого он может игнорировать эти квалификаторы. Я думаю, это означает, что некоторые компиляторы могут не оптимизировать код, содержащий константную переменную, которая будет представлена как промежуточное значение в машинный код (например, #define), и это может стоить в дополнительное время для доступа к большой памяти и воздействия производительности.
Ответ 8
Некоторые компиляторы C будут хранить все переменные const
в двоичном формате, которые при подготовке большого списка коэффициентов могут использовать огромное количество пространства во встроенных мир.
И наоборот: использование const
позволяет мигать над существующей программой для изменения определенных параметров.
Ответ 9
Лучший способ определения числовых констант в C - использование перечисления. Прочтите соответствующую главу K & R Язык программирования C, стр. 39.