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

Указание размера типа перечисления в C

Уже прочитал этот связанный вопрос, но искал что-то более конкретное.

  • Есть ли способ рассказать вашему компилятору конкретно, насколько широка вы хотите, чтобы ваше перечисление было?
  • Если да, то как вы это делаете? Я знаю, как указать его в С#; это аналогично сделано в C?
  • Было бы даже полезно? Когда значение перечисления передается функции, будет ли оно передаваться как int -размерное значение независимо?
4b9b3361

Ответ 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.