Про значения констант enum в C/C++

Перечисление (enum) в языках C/C++ это набор именованных целочисленных констант. В этой статье несколько слов о создании перечисления, его использовании и некоторых особенностях задания констант. Для самых маленьких 🙂 изучающих язык C или C++.

Объявление перечисления

Для того, что бы объявить перечисление нужно сделать следующее:

  • указать ключевое слово enum;
  • в фигурных скобках перечислить имена констант.

Например, так:

enum {Jan, Feb, Mar};

Я предпочитаю так:

enum {
    Jan, 
    Feb, 
    Mar,
};

Обратите внимание, что запятая после последнего значения не является ошибкой. Это нормально понимаемый компилятором синтаксис, и есть причины ставить эту запятую (см. VCS, merge).

После ключевого слова enum можно указать тег этого перечисления (тег это что-то типа имени), например так:

enum Month_t {
...
};

(Многоточие в данном случае не является частью синтаксиса, а просто призвано показать показать, что там есть пропущенные определения констант). Суффикс “_t” не обязателен, Вы можете использовать любое, удобное для Вас имя, но такой суффикс часто используется.

Или Вы можете воспользоваться синонимом типа, например, так:

typedef enum {
...
} Month;

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

Использование перечисления

Теперь Вы можете создать переменную такого типа:

enum Month_t myMonth;

Или вот так:

Month myMonth;

В действующем стандарте C++ компилятор позволяет использовать имя тега, так как будто, объявлен такой синоним, то есть так:

Month_t myMonth;

Но не в языке C! Тег можно использовать только вместе с ключевым словом enum. Поэтому, если Вы предполагаете, что какой-то кусок Вашего кода будет компилироваться и компилятором C и компилятором C++, то пользоваться такой конструкцией не следует – используйте typedef.

Теперь этой переменной Вы можете присвоить значение, например так:

myMonth = Feb;

Однако, нужно помнить, что enum это всего лишь набор целочисленных констант,  как и было сказано в самом начале. То есть, мы можем сделать вот так:

int a;
a = Feb;

Нас никто не ограничивает. Более того, поскольку и сама переменная перечисление, это всего навсего целое число, то мы можем сделать и так:

myMonth = 5;

Правда только для C. Ну есть такие компиляторы и/или статические анализаторы, которые нас хотя бы предупредят, что есть потенциальная проблема, но это не является ошибкой с точки зрения компилятора.

А вот C++ такого не допустит и здесь требуется ручное преобразование типа.

Не трудно догадаться, что и такая конструкция будет работать для языка C:

typedef enum {
    Monday,
    Tuesday,
    Wednesday,
    ...
} WeekDays;

myMonth = Tuesday;

Правда, опять-же, только для языка C. То есть, компилятор обычно в таких случаях выдает предупреждение, о том что есть потенциальная проблема, но ошибкой это не является. Компилятор C не имеет права запрещать Вам использовать перечисления неправильно. А вот компилятор C++, опять-же считает это ошибкой.

Значения констант объявленных в перечислении

Возникает вопрос, а какое же значение будет присвоено переменной a, как в примере выше, в случае, если мы ей присвоим значение Feb? Давайте с этим разберемся. По умолчанию все константы в перечислении получают значение от 0 (для самой первой константы) и далее с инкрементом. То есть, для нашего перечисления Month значения констант получаться:

  • Jan равно 0
  • Feb равно 1
  • Mar равно 2

И так далее, если мы продолжим, это перечисление для всех месяцев в году, Dec получит значение 11. То есть, отвечая на наш вопрос, переменной a будет присвоено значение 1, так как ей присваивали значение Feb.

Иногда нас это не устраивает. Или просто мы не верим значениям по умолчанию, и мы хотим задать константам другие, нужные нам значения. Мы можем это сделать:

enum {
    Jan=1, 
    Feb, 
    Mar,
};

тогда, для константы Jan будет использовано не 0, как по умолчанию, а 1, как мы указали. А далее также с инкрементом:

  • Jan равно 1
  • Feb равно 2
  • Mar равно 3

В более сложных случаях, нам может потребоваться задавать собственное значение каждой константе:: 

enum {
    Jan=2, 
    Feb=10, 
    Mar=2,
};

Да-да. Мы использовали одно и тоже значение дважды. Это не криминал. Это допустимо. Все константы получат в точности указанное нами значение.

И, наконец, рассмотрим, например, такой случай:

enum {
    Jan, 
    Feb=4, 
    Mar,
};

В этом случае, Jan получит значение 0, Feb значение 4, как мы и указали, а дальше включится автоинкремент и Mar получит значение 5.

Замечания по именованию констант

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

typedef enum {
    DayWeekMonday,
    DayWeekTuesday,
    DayWeekWednesday,
} DayWeek;

Фиксы

Update 2014-01-28 18:15 OMST: исправлены некоторые ошибки по поводу отличия между C и C++ по преобразованию перечислений из целого числа и из другого перечислоения и использования тега перечисления вместо синонима типа. Спасибо Максиму Сизоненко за указания на ошибки.

 

Leave a Reply