Почему размер enum
всегда 2 или 4 байта (соответственно 16- или 32-битная архитектура), независимо от количества счетчиков в типе?
Рассматривает ли компилятор enum
как union
?
Почему размер enum
всегда 2 или 4 байта (соответственно 16- или 32-битная архитектура), независимо от количества счетчиков в типе?
Рассматривает ли компилятор enum
как union
?
В C и С++ размер типа enum
определяется реализацией и совпадает с размером некоторого целочисленного типа.
Общий подход заключается в том, чтобы все типы enum
имели тот же размер, что и int
, просто потому, что обычно это тип, который обеспечивает наиболее эффективный доступ. Например, создание одного байта позволит сэкономить очень мало места, но для его доступа может потребоваться больший и медленный код, в зависимости от архитектуры процессора.
В C константы перечисления по определению имеют тип int
. Итак, дано:
enum foo { zero, one, two };
enum foo obj;
выражение zero
имеет тип int
, но obj
имеет тип enum foo
, который может иметь или не иметь тот же размер, что и int
. Учитывая, что константы имеют тип int
, имеет тенденцию быть проще сделать перечислимый тип того же размера.
В С++ правила разные; константы имеют нумерованный тип. Но опять-таки, часто для большинства типов enum
для каждого типа enum
наиболее часто используется одно слово, которое обычно имеет размер int
по соображениям эффективности.
И стандарт ISO С++ 2011 года добавил возможность указать базовый целочисленный тип для типа enum
. Например, теперь вы можете написать:
enum foo: unsigned char { zero, one, two };
что гарантирует, что тип foo
и константы zero
, one
и two
имеют размер 1 байт. C не имеет этой функции и не поддерживается более старыми компиляторами С++ до 2011 года (если только они не предоставляют его как расширение языка).
(Далее следует отступление).
Итак, что, если у вас есть константа перечисления, слишком большая, чтобы вписаться в int
? Вам не нужно 2 31 или даже 2 15 для этого нужно сделать различные константы:
#include <limits.h>
enum huge { big = INT_MAX, bigger };
Значение big
равно INT_MAX
, обычно 2 31 -1, но может быть как 2 15 -1 (32767). Значение bigger
неявно big + 1
.
В С++ это нормально; компилятор просто выберет базовый тип для huge
, который будет достаточно большим, чтобы сохранить значение INT_MAX + 1
. (Предположим, что существует такой тип, если int
- 64 бита, и нет целого типа, большего, чем это, это будет невозможно).
В C, поскольку константы перечисления имеют тип int
, вышесказанное недействительно. Он нарушает ограничение, указанное в N1570 6.7.2.2p2:
Выражение, определяющее значение константы перечисления, должно быть целочисленным постоянным выражением, которое имеет значение, представляемое как Int.
и поэтому компилятор должен отклонить его или, по крайней мере, предупредить об этом. gcc, например, говорит:
ошибка: переполнение в значениях перечисления
Перечисление не является структурой, это просто способ присвоения имен целым числам. Размер переменной с этим типом - это только размер базового целочисленного типа.
Размер перечисления определяется реализацией - компилятору разрешено выбирать любой желаемый размер, если он достаточно велик, чтобы соответствовать всем значениям. Некоторые компиляторы предпочитают использовать 4-байтные перечисления для всех типов перечислений, тогда как некоторые компиляторы будут выбирать наименьший тип (например, 1, 2 или 4 байта), который может соответствовать значениям перечисления. Стандарты языка C и С++ позволяют оба этих поведения.
Из C99 §6.7.2.2/4:
Каждый перечисленный тип должен быть совместим с
char
, целочисленным типом со знаком или беззнаковый целочисленный тип. Выбор типа определяется реализацией, 110) но должен быть способный представлять значения всех членов перечисления.
Из С++ 03 §7.2/5:
Основной тип перечисления является интегральным типом, который может представлять все значения счетчика определенных в перечислении. Определяется реализация, который является интегральным типом в качестве базового типа для перечисления, за исключением того, что базовый тип не должен превышать
int
, если значение счетчика не может помещаться вint
илиunsigned int
. Если список перечислений пуст, базовый тип как если бы перечисление имело единственный перечислитель со значением 0. Значениеsizeof()
, применяемое к перечислению тип, объект типа перечисления или перечислитель - это значениеsizeof()
, применяемое к базовый тип.
Мне кажется, что ОП предположил, что перечисление представляет собой некоторую коллекцию, в которой хранятся объявленные в ней значения. Это неверно.
Перечисление в C/С++ - это просто числовая переменная со строго определенным диапазоном значений. Имена перечисления - это псевдонимы для чисел.
На размер хранилища не влияет количество значений в перечислении. Размер хранилища определяется реализацией, но в основном это sizeof(int)
.
Размер enum
является "интегральным типом, по крайней мере, достаточно большим, чтобы содержать любые значения, указанные в объявлении". Многие компиляторы будут использовать int
(возможно, unsigned
), но некоторые будут использовать char
или short
, в зависимости от оптимизации или других факторов. enum
с менее чем 128 возможными значениями будет соответствовать char
(256 для unsigned char
), и вам придется иметь 32768 (или 65536) значений для переполнения short
и либо 2 или 4 миллиарда значения для перехода int
в большинство современных систем.
An enum
- это, по сути, просто лучший способ определить кучу разных констант. Вместо этого:
#define FIRST 0
#define SECOND 1
...
вы просто:
enum myenum
{ FIRST,
SECOND,
...
};
Это помогает избежать ошибочного присваивания повторяющихся значений и устраняет необходимость даже заботиться о том, каковы конкретные значения (если вам действительно не нужно).
Большая проблема с созданием типа enum
меньше int
, когда меньший тип может соответствовать всем значениям, состоит в том, что он сделает ABI для единицы перевода зависящей от числа констант перечисления. Например, предположим, что у вас есть библиотека, которая использует тип enum
с 256 константами как часть своего открытого интерфейса, а компилятор выбирает представление типа в виде одного байта. Теперь предположим, что вы добавляете новую функцию в библиотеку и теперь нуждаетесь в 257 константах. Компилятор должен был бы переключиться на новый размер/представление, и теперь все объектные файлы, скомпилированные для старого интерфейса, были бы несовместимы с вашей обновленной библиотекой; вам придется перекомпилировать все, чтобы он снова работал.
Таким образом, любая нормальная реализация всегда использует типы int
для enum
.