Почему большинство разработчиков 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.