Подтвердить что ты не робот

Что делает лучшую константу в C, макросе или перечислении?

Я смущен тем, когда использовать макросы или перечисления. Оба могут использоваться как константы, но в чем разница между ними и в чем преимущество одного из них? Это как-то связано с уровнем компилятора или нет?

4b9b3361

Ответ 1

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

Сравнение

#define UNKNOWN  0
#define SUNDAY   1
#define MONDAY   2
#define TUESDAY  3
...
#define SATURDAY 7

к

typedef enum {
    UNKNOWN
,   SUNDAY
,   MONDAY
,   TUESDAY
,   ...
,   SATURDAY
} Weekday;

Намного легче читать код, подобный этому

void calendar_set_weekday(Weekday wd);

чем это

void calendar_set_weekday(int wd);

потому что вы знаете, какие константы должны пройти.

Ответ 2

Макрос - это препроцессор, а скомпилированный код не имеет понятия об создаваемых вами идентификаторах. Они уже заменены препроцессором до того, как код попадает в компилятор. Перечисление - это объект времени компиляции, а скомпилированный код сохраняет полную информацию о символе, который доступен в отладчике (и других инструментах).

Предпочитайте перечисления (когда вы можете).

Ответ 3

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

  • перечислены подписываются (совместимы с int). В любом контексте, где требуется неподписанный тип (подумайте, в частности, побитовые операции!), Перечисления отсутствуют.
  • если long long шире int, большие константы не будут вписываться в перечисление.
  • Размер перечисления sizeof(int). Для массивов небольших значений (например, CHAR_MAX) вам может понадобиться char foo[], а не массив enum foo[].
  • перечисления - это целые числа. У вас не может быть enum funny_number { PI=3.14, E=2.71 }.
  • перечисления - это функция C89; K & R-компиляторы (по общему признанию, древние) не понимают их.

Ответ 4

В C лучше всего использовать перечисления для фактических перечислений: когда какая-либо переменная может содержать одно из нескольких значений, которым могут быть присвоены имена. Одно из преимуществ перечислений состоит в том, что компилятор может выполнять некоторые проверки, кроме того, что требуется языку, например, что оператор switch для типа перечисления не пропускает один из этих случаев. Идентификаторы перечисления также распространяются на информацию об отладке. В отладчике вы можете увидеть имя идентификатора как значение переменной enum, а не только числовое значение.

Перечисления могут использоваться только для побочного эффекта создания символических констант интегрального типа. Например:

enum { buffer_size = 4096 };  /* we don't care about the type */

эта практика не так широко распространена. Во-первых, buffer_size будет использоваться как целое, а не как перечислимый тип. Отладчик не будет отображать 4096 в buffer_size, потому что это значение не будет представлено как перечисляемый тип. Если вы объявите несколько char array[max_buffer_size];, то sizeof array не будет отображаться как buffer_size. В этой ситуации константа перечисления исчезает во время компиляции, поэтому она также может быть макросом. И есть недостатки, такие как неспособность контролировать его точный тип. (В некоторой ситуации может быть какое-то небольшое преимущество, когда вывод стадий предварительной обработки перевода фиксируется как текст. Макрос превратится в 4096, тогда как buffer_size останется как buffer_size).

Символ препроцессора позволяет нам сделать это:

#define buffer_size 0L /* buffer_size is a long int */

Обратите внимание, что различные значения из C <limits.h>, такие как UINT_MAX, являются символами препроцессора, а не перечисляемыми символами, что является веским основанием для этого, поскольку эти идентификаторы должны иметь точно определенный тип. Другим преимуществом символа препроцессора является то, что мы можем проверить его присутствие или даже принимать решения на основе его значения:

#if ULONG_MAX > UINT_MAX 
/* unsigned long is wider than unsigned int */
#endif

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

Перечисления также плохо подходят для битмаксов:

enum modem_control { mc_dsr = 0x1, mc_dtr = 0x2, mc_rts = 0x4, ... }

это просто не имеет смысла, поскольку, когда значения объединены с побитовым ИЛИ, они производят значение, которое находится за пределами типа. Такой код также вызывает головную боль, если он портирован на С++, который имеет (несколько более) нумерацию типов.

Ответ 5

Если макрос реализован правильно (т.е. он не страдает от проблем с ассоциативностью при замене), то нет большой разницы в применимости между константами макроса и enum в ситуациях, когда они применимы, т.е. в ситуации, когда вам нужны фиксированные целочисленные константы.

Однако в общем случае макросы обеспечивают гораздо более гибкую функциональность. Enums накладывают определенный тип на ваши константы: они будут иметь тип int (или, возможно, более крупный тип целочисленного знака), и они всегда будут подписаны. С помощью макросов вы можете использовать постоянные синтаксисы, суффиксы и/или явные преобразования типов для создания константы любого типа.

Enums работают лучше всего, когда у вас есть группа тесно связанных последовательных целочисленных констант. Они работают особенно хорошо, когда вы вообще не заботитесь о фактических значениях констант, т.е. Когда вы заботитесь только о том, чтобы у них были некоторые хорошие значения. Во всех остальных случаях макросы - лучший выбор (или, в основном, единственный выбор).

Ответ 6

В практическом плане мало различий. Они одинаково полезны в качестве констант в ваших программах. Некоторые могут предпочесть один или другой по стилистическим причинам, но я не могу придумать каких-либо технических причин, чтобы предпочесть друг друга.

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

#define X 100L
enum { Y = 100L };

printf("%ld\n", X);
printf("%d\n", Y); /* Y has int type */