В чем разница между использованием инструкции define и оператора перечисления в C/С++ (и есть ли разница при использовании их с C или С++)?
Например, когда следует использовать
enum {BUFFER = 1234};
над
#define BUFFER 1234
В чем разница между использованием инструкции define и оператора перечисления в C/С++ (и есть ли разница при использовании их с C или С++)?
Например, когда следует использовать
enum {BUFFER = 1234};
над
#define BUFFER 1234
enum
определяет синтаксический элемент.
#define
является пред-препроцессорной директивой, выполняемой до того, как компилятор видит код и, следовательно, не является элементом языка самого C.
В общем случае перечисления предпочтительнее, поскольку они являются безопасными по типу и более легко обнаруживаются. Определения сложнее найти и могут иметь сложное поведение, например, один фрагмент кода может переопределить #define
, сделанный другим. Это может быть трудно отследить.
#define
обрабатываются предварительным процессором, прежде чем компилятор получит код, чтобы он в основном заменил текст (он на самом деле немного более интеллектуальный с использованием параметров и т.д.).
Перечисления являются частью самого языка C и имеют следующие преимущества.
1/Они могут иметь тип и компилятор может их проверить.
2/Поскольку они доступны компилятору, информация о символах на них может быть передана в отладчик, что облегчает отладку.
Определить - это команда препроцессора, это просто как "заменить все" в вашем редакторе, он может заменить строку другим, а затем скомпилировать результат.
Enum - это особый случай типа, например, если вы пишете:
enum ERROR_TYPES
{
REGULAR_ERR =1,
OK =0
}
существует новый тип, называемый ERROR_TYPES. Верно, что REGULAR_ERR дает значение 1, но приведение из этого типа в int должно вызвать предупреждение о кастинге (если вы настроите свой компилятор на высокую многословность).
Резюме: они одинаковы, но при использовании перечисления вы получаете доступ к проверке типа и используя определения, вы просто заменяете строки кода.
Перечисления обычно предпочтительнее, чем #define, где имеет смысл использовать перечисление:
enum
( "openType: OpenExisting
", а не "openType: 2
" #define
ition.Самое большое отличие состоит в том, что вы можете использовать перечисления как типы:
// Yeah, dumb example
enum OpenType {
OpenExisting,
OpenOrCreate,
Truncate
};
void OpenFile(const char* filename, OpenType openType, int bufferSize);
Это дает вам проверку типов параметров (вы не можете легко смешивать openType и bufferSize) и позволяет легко находить, какие значения действительны, что делает ваши интерфейсы намного проще в использовании. Некоторые IDE могут даже дать вам завершение кода intellisense!
Всегда лучше использовать перечисление, если это возможно. Использование перечисления дает компилятору больше информации о вашем исходном коде, определение препроцессора никогда не рассматривается компилятором и, следовательно, несет меньше информации.
Для реализации, например, куча режимов, используя перечисление, позволяет компилятору, например, поймать отсутствующие case
-статы в коммутаторе.
enum может группировать несколько элементов в одну категорию:
enum fruits{ apple=1234, orange=12345};
а #define может создавать только несвязанные константы:
#define apple 1234
#define orange 12345
#define - команда препроцессора, перечисление - на языке C или С++.
Всегда лучше использовать перечисления для #define для таких случаев. Одна вещь - безопасность типа. Другим является то, что, когда у вас есть последовательность значений, вам нужно только указать начало последовательности в перечислении, другие значения получат последовательные значения.
enum {
ONE = 1,
TWO,
THREE,
FOUR
};
вместо
#define ONE 1
#define TWO 2
#define THREE 3
#define FOUR 4
В качестве побочной заметки все же есть некоторые случаи, когда вам может понадобиться использовать #define (как правило, для некоторых макросов, если вам нужно построить идентификатор, который содержит константу), но этот вид макро черная магия, и очень очень редко, чтобы быть способ пойти. Если вы идете на эти конечности, вам, вероятно, следует использовать шаблон С++ (но если вы застряли с C...).
Если вам нужна только эта константа (скажем, для буферизации), я бы не использовал перечисление, а определял. Я бы использовал перечисления для таких вещей, как возвращаемые значения (что означало разные условия ошибки) и везде, где нам нужно различать разные "типы" или "случаи". В этом случае мы можем использовать перечисление для создания нового типа, который мы можем использовать в прототипах функций и т.д., А затем компилятор может проверить, лучше ли этот код.
Помимо всего, что уже написано, один сказал, но не показан и интересен. Например.
enum action { DO_JUMP, DO_TURNL, DO_TURNR, DO_STOP };
//...
void do_action( enum action anAction, info_t x );
Рассмотрение действия как типа делает вещи более ясными. Используя define, вы бы написали
void do_action(int anAction, info_t x);
Для интегральных значений констант я предпочитаю enum
более #define
. Кажется, нет никаких недостатков в использовании enum
(дисконтирование минимального недостатка в немного более типизации), но у вас есть преимущество в том, что enum
можно ограничить, а идентификаторы #define
имеют глобальную область видимости, которая тромбирует все.
Использование #define
обычно не является проблемой, но поскольку для enum
нет недостатков, я иду с этим.
В С++ я обычно предпочитаю от enum
до const int
, хотя в С++ a const int
можно использовать вместо значения буквального целого (в отличие от C), потому что enum
переносится на C (который я все еще много работают).
Если у вас есть группа констант (например, "Дни недели" ), перечисления будут предпочтительнее, потому что это показывает, что они сгруппированы; и, как сказал Джейсон, они безопасны по типу. Если это глобальная константа (например, номер версии), это больше, чем вы использовали бы #define
for; хотя это является предметом большого обсуждения.
В дополнение к хорошим пунктам, перечисленным выше, вы можете ограничить область перечислений классом, структурой или пространством имен. Лично мне нравится иметь минимальное количество релевантных символов в области видимости в любой момент, что является еще одной причиной использования перечислений, а не #defines.
Еще одно преимущество перечисления над списком определений заключается в том, что компиляторы (по крайней мере, gcc) могут генерировать предупреждение, когда не все значения проверяются в инструкции switch. Например:
enum {
STATE_ONE,
STATE_TWO,
STATE_THREE
};
...
switch (state) {
case STATE_ONE:
handle_state_one();
break;
case STATE_TWO:
handle_state_two();
break;
};
В предыдущем коде компилятор может генерировать предупреждение о том, что не все значения перечисления обрабатываются в коммутаторе. Если состояния были выполнены как # define, это не так.
перечисления больше используются для перечисления какого-либо набора, например дней в неделю. Если вам нужно только одно постоянное число, const int
(или double и т.д.) Будет определенно лучше, чем перечисление. Мне лично не нравится #define
(по крайней мере, не для определения некоторых констант), потому что он не дает мне типа безопасности, но вы можете, конечно, использовать его, если он вам подходит.
Создание enum создает не только литералы, но и тип, который группирует эти литералы: это добавляет семантику вашему коду, который компилятор может проверить.
Кроме того, при использовании отладчика у вас есть доступ к значениям литералов перечисления. Это не всегда относится к #define.
Enum:
1. Обычно используется для нескольких значений
2. В перечислении есть две вещи: одно имя, а другое - значение имени, которое должно быть выделено, но значение может быть одинаковым. Если мы не определяем значение, тогда первое значение имени перечисления равно 0 секунде 1 и т.д., Если явно не указано значение.
3. У них может быть тип и компилятор, который может набрать их.
4. Сделать отладку проще
5. Мы можем ограничить его объем до класса.
Определить:
1. Когда нам нужно определить только одно значение
2. Он обычно заменяет одну строку на другую.
3.. Область видимости глобальна, мы не можем ограничить ее область.
В целом мы должны использовать enum
Есть небольшая разница. В стандарте C говорится, что перечисления имеют интегральный тип и что константы перечисления имеют тип int, поэтому оба могут свободно перемешаться с другими интегральными типами без ошибок. (Если, с другой стороны, такое смешение было запрещено без явных приемов, разумное использование перечислений могло бы поймать определенные ошибки программирования.)
Некоторые преимущества перечислений заключаются в том, что числовые значения автоматически назначаются, что отладчик может отображать символические значения при проверке переменных перечисления и что они подчиняются области блока. (Компилятор также может генерировать нефатальные предупреждения, когда перечисления смешиваются без разбора, так как делать это можно по-прежнему считаться плохим стилем, даже если он не является строго незаконным.) Недостатком является то, что программист не имеет большого контроля над этими нефатальными предупреждениями; некоторые программисты также возмущены отсутствием контроля над размерами переменных перечисления.
В то время как несколько ответов выше рекомендуют использовать перечисление по разным причинам, я хотел бы указать, что использование определений имеет фактическое преимущество при разработке интерфейсов. Вы можете ввести новые параметры, и вы можете позволить им использовать их условно.
Например:
#define OPT_X1 1 /* introduced in version 1 */ #define OPT_X2 2 /* introduced in version 2 */
Затем программное обеспечение, которое может быть скомпилировано с любой версией, может выполнять
#ifdef OPT_X2 int flags = OPT_X2; #else int flags = 0; #endif
В то время как при перечислении это невозможно без механизма обнаружения функции времени выполнения.