Преимущество и недостатки #define по сравнению с константами? [дубликат]

14
задан Cristian Ciupitu 8 December 2014 в 06:31
поделиться

9 ответов

Константы позволяют указать тип данных, что (обычно) является преимуществом. Макросы гораздо более гибкие, и поэтому могут доставить вам гораздо больше неприятностей, если вы не будете осторожны.

Лучшая практика - использовать константы как можно больше, и использовать #define только тогда, когда вам действительно нужен макрос, а не просто именованное буквальное значение.

.
17
ответ дан 1 December 2019 в 05:53
поделиться
  1. const производит значение lvalue, что означает, что его адрес может быть взят. #define не производит.
  2. #define может привести к непреднамеренным макрорасширениям, которые могут быть PITA для отладки.
  3. Как упоминали другие, #define не имеет ассоциированного с ним типа.

В общем, я бы избегал препроцессора как чумы за все, для чего мне не пришлось бы его использовать, в основном из-за возможности непреднамеренного расширения, а также из-за того, что конвенция ALL_CAPS для смягчения этого невероятно уродлива.

0
ответ дан 1 December 2019 в 05:53
поделиться

Как упоминал 0A0D, есть #defines, enums, и const переменные. Стоит отметить, что const-квалифицированные переменные не считаются константами компилирующего времени в языке Си и поэтому не могут быть использованы в некоторых обстоятельствах (например, при объявлении размера массива). Однако

enum -константы являются константами компилирующего времени. Для интегральных значений IMO обычно лучше предпочесть переменные enum, а не const, а не #define.

.
25
ответ дан 1 December 2019 в 05:53
поделиться

Константы следуют за мерами безопасности типа, #defines заменяются полностью. Также, как сказал GMan, #define's не уважает сферу применения.

3
ответ дан 1 December 2019 в 05:53
поделиться

Const - это, например, объект, адрес которого можно взять. Также она безопасна по типу, т.е. компилятор знает, какой тип константы. Вышеуказанное не относится к #define.

.
0
ответ дан 1 December 2019 в 05:53
поделиться

На самом деле существует три способа определения таких констант,

  • определяет

  • enums
  • const variables

В языке C все является int, если не указано иное. Я предпочитаю перечисления, когда у меня есть несколько связанных целочисленных констант. Перечисления явно предпочтительны, когда вам все равно, что это за значения. Но даже когда нужно указать значения для всех констант, мне нравится ментальная группировка перечислений. Сама по себе кодировка лучше, когда у вас есть тип, например

Error MyFunc();

явно возвращает один из определённого набора кодов ошибок, в то время как

int MyFunc()

может вернуть один из #define'd списка для Unix errno, или, может быть, что-то ещё, или, может быть, те, что плюс некоторые идиосинкратические значения -- кто знает? Если у вас более одного набора кодов возврата, какой набор используется этой функцией?

Более специфическое имя типа enum помогает подсистеме тэгов в вашем редакторе, greps, отладке и т.д.

Строгая подсказка может дать вам несколько предупреждений об использовании enum в качестве целых чисел, например, если вы добавляете или передаете enum в int.

Объект const отличается от перечисления или #define, особенно на Си. В ANSI Си const int занимает место как обычный int; большинство компиляторов также будут генерировать ссылки на указатель на этот адрес, а не подчеркивать значение. В результате я редко использую const int в Си (в Си++ семантика немного другая, и поэтому выбор там разный)

Каждый компилятор, который я когда-либо использовал, имеет возможность хранить перечисления в как можно более маленьком пространстве. Обычно это даже вариант по умолчанию. Чтобы заставить более широкие перечисления при использовании такой опции, я обычно бросаю лишнее беззнаковое значение:

typedef enum
{
    MyEnumA,
    MyEnumB,

    MyEnumForce16 = 7fff
} MyEnum;

Использование константы перечисления (enum) имеет много преимуществ по сравнению с использованием традиционного символического стиля константы #define. К этим преимуществам относятся меньшая потребность в обслуживании, улучшенная читабельность программы и лучшие возможности отладки.

1) Первое преимущество заключается в том, что перечисленные константы генерируются компилятором автоматически. И наоборот, символьные константы должны присваиваться программистом вручную.

Например, если бы у Вас был перечисляемый тип константы для кодов ошибок, которые могут возникнуть в Вашей программе, определение перечисления могло бы выглядеть следующим образом:

enum Error_Code
{
OUT_OF_MEMORY,
INSUFFICIENT_DISK_SPACE,
LOGIC_ERROR,
FILE_NOT_FOUND
};

В предыдущем примере OUT_OF_MEMEMORY компилятору автоматически присваивается значение 0 (ноль), так как оно появляется первым в определении. Затем компилятор продолжает автоматически присваивать числа перечисленным константам, делая INSUFFICIENT_DISK_SPACE равной 1, LOGIC_ERROR равной 2, и FILE_NOT_FOUND равной 3, и так далее. Если бы вы подошли к одному и тому же примеру с использованием символьных констант, ваш код выглядел бы примерно так:

#define OUT_OF_MEMORY 0
#define INSUFFICIENT_DISK_SPACE 1
#define LOGIC_ERROR 2
#define FILE_NOT_FOUND 3

Каждый из двух методов дает один и тот же результат: четыре константы присваивают числовые значения, представляющие собой коды ошибок. Однако, рассмотрим необходимое обслуживание, если Вы добавите две константы для представления кодов ошибок DRIVE_NOT_READY и CORRUPT_FILE. Используя метод констант перечисления, вы просто поместите эти две константы в любое место в определении перечисления. Компилятор сгенерирует два уникальных значения для этих констант. Используя метод символьных констант, нужно было бы вручную присвоить этим константам два новых числа. Дополнительно, вы бы хотели убедиться, что номера, которые вы назначаете этим константам, уникальны.

2) Другое преимущество использования метода констант перечисления состоит в том, что ваши программы более читабельны и поэтому могут быть лучше поняты другими, которым, возможно, придется обновить вашу программу позже.

3) Третье преимущество использования констант перечисления состоит в том, что некоторые символические отладчики могут распечатывать значение константы перечисления. И наоборот, большинство символьных отладчиков не могут вывести значение символьной константы. Это может быть огромной помощью в отладке вашей программы, потому что если ваша программа останавливается на строке, которая использует перечисление, вы можете просто проверить эту константу и мгновенно узнать ее значение. С другой стороны, поскольку большинство отладчиков не могут распечатать значения #define, то, скорее всего, вам придется искать это значение вручную в заголовочном файле.

Оператор #define является директивой прекомпилятора. Технически, любая строка, которая начинается с #, является чем-то, над чем должен работать пре-компилятор. Предскомпилятор заменит все экземпляры определенной лексемы на ее определение. Таким образом:

#define DELAY 40
for (i=0;i<DELAY;i++) {
    for (j=0;j<DELAY;j++) {
        asm NOP;
    }
}

точно так же (как и компилятор):

for (i=0;i<40;i++) {
    for (j=0;j<40;j++) {
        asm NOP;
    }
}

Когда компилятор генерирует машинный код, он увидит число 40 и воспользуется режимом немедленной адресации, чтобы сравнить с аккумулятором. Число 40 будет храниться в коде столько раз, сколько вы на него ссылаетесь. В этом случае оно встречается дважды. Вот ассемблера, сгенерированная CodeWarrior Ver5:

7:    char i,j;
    8:    for (i=0;i<DELAY;i++) {
  0002 95       [2]             TSX   
  0003 7f       [2]             CLR   ,X
  0004          [5]     L4:     
    9:      for (j=0;j<DELAY;j++) {
  0004 6f01     [3]             CLR   1,X
  0006          [5]     L6:     
   10:        asm NOP;
  0006 9d       [1]             NOP   
  0007 6c01     [4]             INC   1,X
  0009 e601     [3]             LDA   1,X
  000b a128     [2]             CMP   #40  ;<---- notice opcode a1 and immediate constant 40, which is $28 in hexadecimal
  000d 25f7     [3]             BCS   L6
  000f 7c       [3]             INC   ,X
  0010 f6       [2]             LDA   ,X
  0011 a128     [2]             CMP   #40  ;<---- and here it is again.
  0013 25ef     [3]             BCS   L4
   11:      }
   12:    }
   13:  }
18
ответ дан 1 December 2019 в 05:53
поделиться

Преимущество констант в том, что они набраны, так что их неправильное использование может быть обнаружено во время компиляции. Для вас это может не иметь значения, но константы занимают место в памяти, а #defines - нет (т.к. они заменяются до реальной компиляции).

4
ответ дан 1 December 2019 в 05:53
поделиться

Объяснение для #define:A #define - это либо непосредственное значение, либо макрос.

Пояснение для константы:Константа - это значение любого типа, которое никогда не может измениться.

Вы можете напугать указатель на константу, но не на #define, хотя #define может быть указателем. например: #define ADDRESS ((int *)0x0012)

Так почему мы должны использовать константу так:

  • они подчиняются правилам языка
  • вы можете увидеть их в отладчике
  • вы можете взять их адрес, если вам нужно
  • вы можете передать их по константе, если вам нужно
  • они не создают новых "ключевых слов" в вашей программе.

Короче говоря, идентификаторы const ведут себя так, как будто они являются частью языка.

Внутри модуля компилятор Си может оптимизировать const, как будто это #define, если на константу не объявлено ни одного указателя. В терминах процессора константа стала бы "непосредственным" значением. Другая альтернатива заключается в том, что переменная const могла бы быть помещается в область кода, а не в область данных, так как не изменяется. На некоторых машинах объявление понтера константе может вызвать исключение, если вы попытаетесь изменить константу с помощью указателя.

Есть случаи, когда #define нужен, но обычно его следует избегать, когда у вас есть выбор. Вы должны оценить, следует ли использовать const или #define, основываясь на стоимости бизнеса: время, деньги, риск.

1
ответ дан 1 December 2019 в 05:53
поделиться

1) #define можно рассматривать как настраиваемые параметры, не зависящие от типов данных, тогда как константа позволяет упомянуть тип данных.

2) #define's заменяет любой код, который следует за этим в основной программе, куда бы они ни ссылались. В дополнение к этому мы даже можем иметь макрофункцию, выполняющую определенную задачу, которая может быть вызвана только передачей параметров. Очевидно, что это невозможно в случае констант.

Таким образом, они используются исходя из релевантности.

.
0
ответ дан 1 December 2019 в 05:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: