Преимущества и недостатки #define против констант?

Может ли кто-нибудь указать преимущества и недостатки использования #define по сравнению с константами? Большая часть моей работы выполняется в C и Objective-C.

Ответ 1

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

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

Ответ 2

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

enum константы - это константы времени компиляции. Для интегральных значений ИМО обычно лучше предпочитать enums над переменными const по сравнению с #define.

Ответ 3

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

  • определяет

  • перечислений
  • константные переменные

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

Error MyFunc();

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

int MyFunc()

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

Более конкретное имя типа перечисления помогает средствам тегов в вашем редакторе, greps, debugging и т.д.

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

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

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

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_MEMORY автоматически присваивает значение 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:  }
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:  }

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

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

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

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

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

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

Ответ 7

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

Ответ 8

  • const создает значение lvalue, то есть его адрес может быть принят. #define нет.
  • #define может вызвать непреднамеренные расширения макросов, которые могут быть отладки PITA.
  • Как упоминалось другими, #define не имеет связанного с ним типа.

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

Ответ 9

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

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

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

Ответ 10

преимущество использования define заключается в том, что вы определяете переменную для exp: #define NUMBER 30, весь код в основном будет использовать этот код со значением 30. Если вы измените значение 30 на 40, оно будет напрямую изменять все значения в main который использует эту переменную (NUMBER).