Почему размер 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.