Указание размера типа enum в C - PullRequest
28 голосов
/ 02 февраля 2011

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

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

Ответы [ 9 ]

18 голосов
/ 02 февраля 2011

Есть ли способ рассказать вашему компилятору в частности, насколько широко вы хотите, чтобы ваш перечисление будет?

В общем случае нет. Не в стандартной С.

Стоит ли это делать?

Это зависит от контекста. Если вы говорите о передаче параметров в функции, то нет, делать это не стоит (см. Ниже). Если речь идет об экономии памяти при построении агрегатов из перечислимых типов, то, возможно, это стоит сделать. Однако в C вы можете просто использовать целочисленный тип подходящего размера вместо типа enum в агрегатах. В C (в отличие от C ++) перечислимые и целочисленные типы почти всегда взаимозаменяемы.

Когда значение перечисления передается в функцию, будет ли оно передано как значение типа int независимо?

Многие (большинство) компиляторы в наши дни передают все параметры как значения естественного размера слова для данной аппаратной платформы. Например, на 64-битной платформе многие компиляторы передают все параметры как 64-битные значения, независимо от их фактического размера, даже если на этой платформе тип int содержит 32 бита (поэтому он обычно не передается как «int-size» значение на такой платформе). По этой причине нет смысла пытаться оптимизировать размеры перечислений для целей передачи параметров.

16 голосов
/ 02 февраля 2011

Я полагаю, что если вы используете GCC, есть флаг.

8 голосов
/ 02 февраля 2011

Вы можете заставить его быть по крайней мере определенного размера, определив соответствующее значение. Например, если вы хотите, чтобы ваш enum сохранялся в том же размере, что и int, даже если все значения помещались бы в char, вы можете сделать что-то вроде этого:

typedef enum {
    firstValue = 1,
    secondValue = 2,

    Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;

Обратите внимание, однако, что поведение может зависеть от реализации.

Как вы заметили, передача такого значения в функцию в любом случае приведет к его расширению до int, но если вы используете ваш тип в массиве или структуре, тогда размер будет иметь значение. Если вы действительно заботитесь о размерах элементов, вам следует использовать такие типы, как int8_t, int32_t и т. Д.

7 голосов
/ 09 июня 2013

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

struct something {
     :0;
   enum whatever field:CHAR_BIT;
     :0;
};

The: 0;может быть опущено, если поле enum окружено обычными полями.Если ранее было другое битовое поле, то: 0 принудительно выровняет байты по следующему байту для поля, следующего за ним.

0 голосов
/ 03 июня 2019

Даже если вы пишете строгий код 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 ++.

EDIT:

@ Подозрение технозавра было верным.

Проверяя размер 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
0 голосов
/ 28 февраля 2019

В некоторых случаях это может быть полезно:

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]

0 голосов
/ 05 февраля 2019

Как @ Nyx0uf говорит , GCC имеет флаг, который вы можете установить:

-fshort-enums

Выделить типу перечисления только столько байтовтак как это необходимо для заявленного диапазона возможных значений.В частности, тип 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

0 голосов
/ 28 июня 2015

Это зависит от значений, назначенных для перечислений.

Пример: Если сохранено значение больше 2 ^ 32-1, размер, выделенный для общего перечисления, изменится на следующий размер.

Сохраните значение 0xFFFFFFFFFFFF в переменной enum, оно выдаст предупреждение при попытке компиляции в 32-битной среде (предупреждение о округлении) Где, как и при 64-битной компиляции, оно будет успешным, а выделенный размер будет 8 байтов.

0 голосов
/ 20 декабря 2011

Другой способ - привести перечисление в союз следующим образом:

union char_size {
  char a;
  enum {
    a = 1,
    b = 2,
  } val;
};

Это заставит компилятор соответствовать перечислению внутри символа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...