Указание размера типа перечисления в C
Уже прочитал этот связанный вопрос, но искал что-то более конкретное.
- Есть ли способ рассказать вашему компилятору конкретно, насколько широка вы хотите, чтобы ваше перечисление было?
- Если да, то как вы это делаете? Я знаю, как указать его в С#; это аналогично сделано в C?
- Было бы даже полезно? Когда значение перечисления передается функции, будет ли оно передаваться как
int
-размерное значение независимо?
Ответы
Ответ 1
Есть ли способ сообщить вашему компилятору в частности, насколько широка ваша enum быть?
В общем случае нет. Не в стандартном C.
Было бы даже полезно?
Это зависит от контекста. Если вы говорите о передаче параметров в функции, то нет, это не стоит делать (см. Ниже). Если речь идет о сохранении памяти при создании агрегатов из типов перечислений, то это может стоить того. Однако в C вы можете просто использовать целочисленный тип подходящего размера вместо типа перечисления в агрегатах. В C (в отличие от С++) типы перечислений и целые типы почти всегда взаимозаменяемы.
Когда значение перечисления передается функции, будет ли оно передаваться как значение размера по умолчанию независимо?
Многие (большинство) компиляторов в эти дни передают все параметры как значения натурального размера слова для данной аппаратной платформы. Например, на 64-битной платформе многие компиляторы передадут все параметры в виде 64-битных значений независимо от их фактического размера, даже если тип int
имеет в нем 32 бита на этой платформе (поэтому он обычно не передается как значение "int-size" на такой платформе). По этой причине нет смысла пытаться оптимизировать размеры перечислений для целей передачи параметров.
Ответ 2
Я считаю, что есть флаг, если вы используете GCC.
-fshort-перечисления
Ответ 3
Вы можете заставить его быть как минимум определенного размера, задав соответствующее значение. Например, если вы хотите, чтобы ваше перечисление было сохранено в размере того же размера, что и int
, хотя все значения будут соответствовать char
, вы можете сделать что-то вроде этого:
typedef enum {
firstValue = 1,
secondValue = 2,
Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;
Обратите внимание, однако, что поведение может зависеть от реализации.
Как вы заметили, передача такого значения функции приведет к тому, что она будет расширена до int в любом случае, но если вы используете свой тип в массиве или структуре, размер будет иметь значение. Если вам действительно нужны размеры элементов, вы должны действительно использовать такие типы, как int8_t
, int32_t
и т.д.
Ответ 4
Существует также другой способ, если перечисление является частью структуры:
struct something {
:0;
enum whatever field:CHAR_BIT;
:0;
};
The: 0; может быть опущено, если поле перечисления окружено нормальными полями. Если есть еще одно битовое поле раньше, то: 0 принудительно выравнивает байты к следующему байту для поля, следующего за ним.
Ответ 5
Это зависит от значений, назначенных для перечислений.
Пример:
Если значение больше 2 ^ 32-1, размер, выделенный для общего перечисления, изменится на следующий размер.
Сохраняйте значение 0xFFFFFFFFFFFF переменной перечисления, оно даст предупреждение, если попытается скомпилировать в 32-битной среде (предупреждение об отключении)
Где, как и в 64-битной компиляции, она будет успешной, а выделенный размер будет 8 байтов.
Ответ 6
Как говорит @Nyx0uf, у GCC есть флаг, который вы можете установить:
-fshort-enums
Выделите типу enum только столько байтов, сколько необходимо для объявленного диапазона возможных значений. В частности, тип enum эквивалентен наименьшему целочисленному типу, который имеет достаточно места.
Предупреждение: ключ -fshort-enums заставляет GCC генерировать код, который не является двоично-совместимым с кодом, созданным без этого ключа. Используйте его для соответствия бинарному интерфейсу приложения не по умолчанию.
Источник: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
Дополнительное отличное чтение для общего понимания: https://www.embedded.fm/blog/2016/6/28/how-big-is-an-enum.
Интересно... обратите внимание на линию, которую я выделил желтым цветом ниже!
![enter image description here]()
Ответ 7
В некоторых случаях это может быть полезно:
typedef uint8_t command_t;
enum command_enum
{
CMD_IDENT = 0x00, //!< Identify command
CMD_SCENE_0 = 0x10, //!< Recall Scene 0 command
CMD_SCENE_1 = 0x11, //!< Recall Scene 1 command
CMD_SCENE_2 = 0x12, //!< Recall Scene 2 command
};
/* cmdVariable is of size 8 */
command_t cmdVariable = CMD_IDENT;
С одной стороны, тип command_t
имеет размер 8 и может использоваться для переменных и типов параметров функций. С другой стороны, вы можете использовать значения enum для присваивания, которые по умолчанию имеют тип int
, но компилятор будет приводить их немедленно при назначении переменной типа command_t
.
Кроме того, если вы делаете что-то небезопасное, например, определяете и используете CMD_16bit = 0xFFFF,
компилятор предупредит вас следующим сообщением:
предупреждение: большое целое число неявно усекается до типа без знака [-Woverflow]
Ответ 8
Даже если вы пишете строгий C
код, результаты будут зависеть от компилятора. Используя стратегии из этой ветки, я получил несколько интересных результатов...
enum_size.c
#include <stdio.h>
enum __attribute__((__packed__)) PackedFlags {
PACKED = 0b00000001,
};
enum UnpackedFlags {
UNPACKED = 0b00000001,
};
int main (int argc, char * argv[]) {
printf("packed:\t\t%lu\n", sizeof(PACKED));
printf("unpacked:\t%lu\n", sizeof(UNPACKED));
return 0;
}
$ gcc enum_size.c
$ ./a.out
packed: 4
unpacked: 4
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed: 4
unpacked: 4
$ g++ enum_size.c
$ ./a.out
packed: 1
unpacked: 4
$ g++ enum_size.c -fshort_enums
$ ./a.out
packed: 1
unpacked: 1
В моем примере выше я не осознавал никакой выгоды от модификатора __attribute__((__packed__))
, пока не начал использовать компилятор C++.
РЕДАКТИРОВАТЬ:
Подозрение @technosaurus было правильным.
sizeof(enum PackedFlags)
размер sizeof(enum PackedFlags)
вместо sizeof(PACKED)
я вижу ожидаемые результаты...
printf("packed:\t\t%lu\n", sizeof(enum PackedFlags));
printf("unpacked:\t%lu\n", sizeof(enum UnpackedFlags));
Теперь я вижу ожидаемые результаты от gcc
:
$ gcc enum_size.c
$ ./a.out
packed: 1
unpacked: 4
$ gcc enum_size.c -fshort_enums
$ ./a.out
packed: 1
unpacked: 1
Ответ 9
Другой способ - перечислить перечисление внутри объединения следующим образом:
union char_size {
char a;
enum {
a = 1,
b = 2,
} val;
};
Это приведет к тому, что компилятор будет соответствовать перечислению внутри char.